From 448762b72c538c2ac6f934d9547868f6cdbd54b0 Mon Sep 17 00:00:00 2001 From: jayd1860 <44243610+jayd1860@users.noreply.github.com> Date: Fri, 3 Mar 2023 00:47:33 -0500 Subject: [PATCH 1/3] Merge v1.72.0: Fix for ar_glm_final.m stepwise error in GLM and fix for export of proc stream mlActMan (#162) Merge v1.72.0: Fix for ar_glm_final.m stepwise error in GLM and fix for export of proc stream mlActMan --- DataTree/AcquiredData/Nirs/NirsClass.m | 118 +++++++++++++++++- DataTree/AcquiredData/Snirf/DataClass.m | 3 + .../AcquiredData/Snirf/MetaDataTagsClass.m | 10 +- DataTree/AcquiredData/Snirf/ProbeClass.m | 42 ++++--- DataTree/AcquiredData/Snirf/SnirfClass.m | 1 - DataTree/GroupClass.m | 17 +-- DataTree/ProcStream/ProcStreamClass.m | 10 +- DataTree/RunClass.m | 22 ++++ DataTree/TreeNodeClass.m | 18 +++ DataTree/Version.txt | 2 +- FuncRegistry/UserFunctions/iWLS/ar_fit.m | 5 +- .../UserFunctions/iWLS/ar_glm_final.m | 2 +- FuncRegistry/Version.txt | 2 +- Utils/Shared/Version.txt | 3 +- Utils/Shared/checkSharedLibCompatibility.m | 29 +++++ Utils/Shared/versionstr2num.m | 9 ++ Version.txt | 3 +- 17 files changed, 253 insertions(+), 43 deletions(-) create mode 100644 Utils/Shared/checkSharedLibCompatibility.m create mode 100644 Utils/Shared/versionstr2num.m diff --git a/DataTree/AcquiredData/Nirs/NirsClass.m b/DataTree/AcquiredData/Nirs/NirsClass.m index ea0ffd8e..13b127d4 100644 --- a/DataTree/AcquiredData/Nirs/NirsClass.m +++ b/DataTree/AcquiredData/Nirs/NirsClass.m @@ -388,7 +388,7 @@ function SaveMat(obj, fname, options) return; end else - if eval( sprintf('~all( (obj.SD.%s(:) - obj2.SD.%s(:)) < EPS )', field, field) ) + if eval( sprintf('~all( abs(obj.SD.%s(:) - obj2.SD.%s(:)) < EPS )', field, field) ) return; end end @@ -1033,6 +1033,7 @@ function RenameCondition(obj, oldname, newname) end + % ---------------------------------------------------------------------------------- function SD = InitProbe(obj) SD = struct(... @@ -1049,7 +1050,6 @@ function RenameCondition(obj, oldname, newname) 'SrcGrommetRot',[], ... 'DetGrommetRot',[], ... 'DummyGrommetRot',[], ... - 'nDummys',0, ... 'Landmarks',obj.InitLandmarks(), ... 'Landmarks2D',obj.InitLandmarks(), ... 'Landmarks3D',obj.InitLandmarks(), ... @@ -1069,6 +1069,59 @@ function RenameCondition(obj, oldname, newname) + % ---------------------------------------------------------------------------------- + function SetProbeSpatialUnit(obj, spatialUnitNew) + scaling = 1; + if strcmpi(spatialUnitNew,'mm') + if strcmpi(obj.SD.SpatialUnit,'cm') + scaling = 10; + end + elseif strcmpi(spatialUnitNew,'cm') + if strcmpi(obj.SD.SpatialUnit,'mm') + scaling = 1/10; + end + else + spatialUnitNew = ''; + end + obj.SD.SpatialUnit = spatialUnitNew; + obj.SD.SrcPos = obj.SD.SrcPos * scaling; + obj.SD.DetPos = obj.SD.DetPos * scaling; + obj.SD.DummyPos = obj.SD.DummyPos * scaling; + if size(obj.SD.SpringList,2)==3 + lst = find(obj.SD.SpringList(:,3)~=-1); + obj.SD.SpringList(lst,3) = obj.SD.SpringList(lst,3) * scaling; + end + obj.SD.Landmarks.pos = obj.SD.Landmarks.pos * scaling; + obj.SD.Landmarks3D.pos = obj.SD.Landmarks3D.pos * scaling; + obj.SD.Landmarks2D.pos = obj.SD.Landmarks2D.pos * scaling; + end + + + + % ---------------------------------------------------------------------------------- + function FixProbeSpatialUnit(obj) + if isempty(obj.SD.SpatialUnit) + q = MenuBox('Spatial units not provided in probe data. Please specify spatial units of the optode coordinates?', ... + {'mm','cm',sprintf('do not know')}); + if q==1 + obj.SD.SpatialUnit = 'mm'; + elseif q==2 + obj.SD.SpatialUnit = 'cm'; + elseif q==3 + obj.SD.SpatialUnit = ''; + end + end + if ~strcmpi(obj.SD.SpatialUnit,'mm') + q = MenuBox(sprintf('This probe uses ''%s'' units for probe coordinates. We recommend converting to ''mm'' units, to be consistent with Homer. Do you want to convert probe coordinates from %s to mm?', ... + obj.SD.SpatialUnit), {'YES','NO'}, 'upperleft'); + if q==1 + obj.SetProbeSpatialUnit('mm') + end + end + end + + + % ---------------------------------------------------------------------------------- function b = IsProbeValid(obj) b = false; @@ -1113,11 +1166,70 @@ function CopyProbe(obj, SD) fields = propnames(obj.SD); for ii = 1:length(fields) if eval( sprintf('isfield(SD, ''%s'')', fields{ii}) ) + if eval( sprintf('strcmp(class(obj.SD.%s), class(SD.%s))', fields{ii}, fields{ii}) ) eval( sprintf('obj.SD.%s = SD.%s;', fields{ii}, fields{ii}) ); + elseif eval( sprintf('isnumeric(obj.SD.%s) && iscell(SD.%s)', fields{ii}, fields{ii}) ) + if eval( sprintf('~isempty(SD.%s)', fields{ii}) ) + eval( sprintf('obj.SD.%s = cell2array(SD.%s);', fields{ii}, fields{ii}) ); end + elseif eval( sprintf('isscalar(obj.SD.%s) && isscalar(SD.%s)', fields{ii}, fields{ii}) ) + eval( sprintf('obj.SD.%s = SD.%s;', fields{ii}, fields{ii}) ); + end + end + end + + % Fill in any fields that don't conform to standard SD data structure + + % SrcGrommetType + d1 = size(obj.SD.SrcPos,1) - length(obj.SD.SrcGrommetType); + if d1 > 0 + obj.SD.SrcGrommetType(end+1:end+d1) = repmat({'none'}, d1, 1); end + + % SrcGrommetRot + d2 = size(obj.SD.SrcPos,1) - length(obj.SD.SrcGrommetRot); + if d2 > 0 + obj.SD.SrcGrommetRot(end+1:end+d2) = zeros(d2,1); + end + + % DetGrommetType + d1 = size(obj.SD.DetPos,1) - length(obj.SD.DetGrommetType); + if d1 > 0 + obj.SD.DetGrommetType(end+1:end+d1) = repmat({'none'}, d1, 1); + end + + % DetGrommetRot + d2 = size(obj.SD.DetPos,1) - length(obj.SD.DetGrommetRot); + if d2 > 0 + obj.SD.DetGrommetRot(end+1:end+d2) = zeros(d2,1); + end + + % DummyGrommetType + d1 = size(obj.SD.DummyPos,1) - length(obj.SD.DummyGrommetType); + if d1 > 0 + obj.SD.DummyGrommetType(end+1:end+d1) = repmat({'none'}, d1, 1); + end + + % DummyGrommetRot + d2 = size(obj.SD.DummyPos,1) - length(obj.SD.DummyGrommetRot); + if d2 > 0 + obj.SD.DummyGrommetRot(end+1:end+d2) = zeros(d2,1); + end + + % MesListAct + if size(obj.SD.MeasListAct,1) < size(obj.SD.MeasList,1) + d = size(obj.SD.MeasListAct,1) - size(obj.SD.MeasList,1); + if d < 0 + obj.SD.MeasListAct(end+1:end+abs(d)) = ones(abs(d),1); + elseif d>1 + obj.SD.MeasListAct(end-d:end) = []; + end end + end + + + % ------------------------------------------------------- function CopyStim(obj, obj2) @@ -1127,7 +1239,7 @@ function CopyStim(obj, obj2) % ---------------------------------------------------------------------------------- - function b = CopyStruct(obj, s) + function CopyStruct(obj, s) fields = propnames(obj); for ii = 1:length(fields) if eval( sprintf('isfield(s, ''%s'')', fields{ii}) ) diff --git a/DataTree/AcquiredData/Snirf/DataClass.m b/DataTree/AcquiredData/Snirf/DataClass.m index 9acc5fc8..542ea28c 100644 --- a/DataTree/AcquiredData/Snirf/DataClass.m +++ b/DataTree/AcquiredData/Snirf/DataClass.m @@ -771,6 +771,9 @@ function SaveHdf5(obj, fileobj, location) ml = obj.measurementList; ml(order) = ml; end + if contains(options, 'reshape') + ml = measurementListSDpairs; + end obj.SimulateErrors(d); end diff --git a/DataTree/AcquiredData/Snirf/MetaDataTagsClass.m b/DataTree/AcquiredData/Snirf/MetaDataTagsClass.m index 65aad95a..3a855ab5 100644 --- a/DataTree/AcquiredData/Snirf/MetaDataTagsClass.m +++ b/DataTree/AcquiredData/Snirf/MetaDataTagsClass.m @@ -16,8 +16,8 @@ obj.tags.LengthUnit = 'mm'; obj.tags.TimeUnit = 'unknown'; obj.tags.FrequencyUnit = 'unknown'; - obj.tags.AppName = 'snirf-homer3'; - + obj.tags.AppName = 'homer3-DataTree'; + if nargin==1 && ~isempty(varargin{1}) obj.SetFilename(varargin{1}); obj.Load(); @@ -83,7 +83,7 @@ end obj.SetError(err); - + end @@ -105,8 +105,8 @@ function SaveHdf5(obj, fileobj, location) %#ok<*INUSD> fid = H5F.create(fileobj, 'H5F_ACC_TRUNC', 'H5P_DEFAULT', 'H5P_DEFAULT'); H5F.close(fid); end - props = propnames(obj.tags); - for ii=1:length(props) + props = propnames(obj.tags); + for ii = 1:length(props) eval(sprintf('hdf5write_safe(fileobj, [location, ''/%s''], obj.tags.%s);', props{ii}, props{ii})); end end diff --git a/DataTree/AcquiredData/Snirf/ProbeClass.m b/DataTree/AcquiredData/Snirf/ProbeClass.m index 94a21213..c3c7a66d 100644 --- a/DataTree/AcquiredData/Snirf/ProbeClass.m +++ b/DataTree/AcquiredData/Snirf/ProbeClass.m @@ -20,6 +20,10 @@ landmarkLabels end + properties (Access = private) + scaling + end + methods @@ -28,11 +32,13 @@ function obj = ProbeClass(varargin) % Set class properties not part of the SNIRF format obj.SetFileFormat('hdf5'); + obj.scaling = 1; % Set SNIRF fomat properties if nargin>0 if isstruct(varargin{1}) - SD = varargin{1}; + n = NirsClass(varargin{1}); + SD = n.SD; obj.wavelengths = SD.Lambda; obj.wavelengthsEmission = []; if isfield(SD,'SrcPos2D') & ~isempty(SD.SrcPos2D) @@ -250,18 +256,16 @@ function Project_3D_to_2D(obj) end % Arg3 - scaling = 1; if exist('LengthUnit','var') - if strcmpi(LengthUnit,'m') - scaling = 1000; - elseif strcmpi(LengthUnit,'cm') - scaling = 10; + % Figure out the scaling factor to multiple by to get the coorinates to be in mm units + if strcmpi(LengthUnit,'m') % meter units + obj.scaling = 100; + elseif strcmpi(LengthUnit,'cm') % centimeter units + obj.scaling = 10; end end - - % Error checking if ~isempty(fileobj) && ischar(fileobj) obj.SetFilename(fileobj); @@ -280,12 +284,12 @@ function Project_3D_to_2D(obj) % Load datasets obj.wavelengths = HDF5_DatasetLoad(gid, 'wavelengths'); obj.wavelengthsEmission = HDF5_DatasetLoad(gid, 'wavelengthsEmission'); - obj.sourcePos2D = HDF5_DatasetLoad(gid, 'sourcePos2D', [], '2D')*scaling; - obj.detectorPos2D = HDF5_DatasetLoad(gid, 'detectorPos2D', [], '2D')*scaling; - obj.landmarkPos2D = HDF5_DatasetLoad(gid, 'landmarkPos2D', [], '2D')*scaling; - obj.sourcePos3D = HDF5_DatasetLoad(gid, 'sourcePos3D', [], '3D')*scaling; - obj.detectorPos3D = HDF5_DatasetLoad(gid, 'detectorPos3D', [], '3D')*scaling; - obj.landmarkPos3D = HDF5_DatasetLoad(gid, 'landmarkPos3D', [], '2D')*scaling; + obj.sourcePos2D = HDF5_DatasetLoad(gid, 'sourcePos2D', [], '2D') * obj.scaling; + obj.detectorPos2D = HDF5_DatasetLoad(gid, 'detectorPos2D', [], '2D') * obj.scaling; + obj.landmarkPos2D = HDF5_DatasetLoad(gid, 'landmarkPos2D', [], '2D') * obj.scaling; + obj.sourcePos3D = HDF5_DatasetLoad(gid, 'sourcePos3D', [], '3D') * obj.scaling; + obj.detectorPos3D = HDF5_DatasetLoad(gid, 'detectorPos3D', [], '3D') * obj.scaling; + obj.landmarkPos3D = HDF5_DatasetLoad(gid, 'landmarkPos3D', [], '2D') * obj.scaling; obj.frequencies = HDF5_DatasetLoad(gid, 'frequencies'); obj.timeDelays = HDF5_DatasetLoad(gid, 'timeDelays'); obj.timeDelayWidths = HDF5_DatasetLoad(gid, 'timeDelayWidths'); @@ -350,6 +354,16 @@ function SaveHdf5(obj, fileobj, location) H5F.close(fid); end + % Multiple all coordinates by reciprocal of scaling factor when saving to get the + % coordinates back to original. + obj.sourcePos2D = obj.sourcePos2D / obj.scaling; + obj.detectorPos2D = obj.detectorPos2D / obj.scaling; + obj.landmarkPos2D = obj.landmarkPos2D / obj.scaling; + obj.sourcePos3D = obj.sourcePos3D / obj.scaling; + obj.detectorPos3D = obj.detectorPos3D / obj.scaling; + obj.landmarkPos3D = obj.landmarkPos3D / obj.scaling; + + % Now save hdf5write_safe(fileobj, [location, '/wavelengths'], obj.wavelengths, 'array'); hdf5write_safe(fileobj, [location, '/wavelengthsEmission'], obj.wavelengthsEmission, 'array'); hdf5write_safe(fileobj, [location, '/sourcePos2D'], obj.sourcePos2D, 'array'); diff --git a/DataTree/AcquiredData/Snirf/SnirfClass.m b/DataTree/AcquiredData/Snirf/SnirfClass.m index 15b2e02a..66ae5852 100644 --- a/DataTree/AcquiredData/Snirf/SnirfClass.m +++ b/DataTree/AcquiredData/Snirf/SnirfClass.m @@ -516,7 +516,6 @@ function SortStims(obj) LengthUnit = obj.metaDataTags.Get('LengthUnit'); obj.probe = ProbeClass(); err = obj.probe.LoadHdf5(fileobj, [obj.location, '/probe'], LengthUnit); - obj.metaDataTags.Set('LengthUnit','mm'); % This is a required field. If it's empty means the whole snirf object is bad if isempty(obj.probe) diff --git a/DataTree/GroupClass.m b/DataTree/GroupClass.m index 0e06b31e..3d048bcd 100644 --- a/DataTree/GroupClass.m +++ b/DataTree/GroupClass.m @@ -71,7 +71,7 @@ % ---------------------------------------------------------------------------------- function InitVersion(obj) - obj.SetVersion(); + obj.SetVersion(getVernum('DataTree')); obj.InitVersionStrFull(); end @@ -848,15 +848,6 @@ function SaveAcquiredData(obj) % ---------------------------------------------------------------------------------- function probe = GetProbe(obj) probe = obj.subjs(1).GetProbe(); -% for subj = obj.subjs -% for sess = subj.sess -% for run = sess.runs -% if ~(probe == run.GetProbe()) -% warning(['Probe ', run.name, ' differs from ', obj.subjs(1).sess(1).runs(1).name]) -% end -% end -% end -% end end @@ -1133,7 +1124,11 @@ function BackwardCompatability(obj) if ~ispathvalid([obj.path, obj.outputDirname]) mkdir([obj.path, obj.outputDirname]); end - movefile(oldDerivedPath, [obj.path, obj.outputDirname]) + + try + movefile(oldDerivedPath, [obj.path, obj.outputDirname]) + catch + end if ispathvalid([obj.path, obj.outputDirname, obj.outputFilename]) if ~strcmp(obj.outputFilename, 'groupResults.mat') diff --git a/DataTree/ProcStream/ProcStreamClass.m b/DataTree/ProcStream/ProcStreamClass.m index 03421adf..9ec5844a 100644 --- a/DataTree/ProcStream/ProcStreamClass.m +++ b/DataTree/ProcStream/ProcStreamClass.m @@ -376,10 +376,16 @@ function SaveInitOutput(obj, pathname, filename) % ---------------------------------------------------------------------------------- function mlActMan = CompressMlActMan(obj) mlActMan = []; - if isempty(obj.input.mlActMan) + + % We don't need to compress mlAct man because it is usually not that big + % But even did then we have to modify compressLogicalArray to handle 2d arrays + % instead of just vectors. + % mlActMan = compressLogicalArray(obj.input.mlActMan{1}); + temp = obj.GetVar('mlActMan'); + if isempty(temp) return end - mlActMan = compressLogicalArray(obj.input.mlActMan{1}); + mlActMan = temp{1}; end diff --git a/DataTree/RunClass.m b/DataTree/RunClass.m index 174ad3c6..539fd440 100644 --- a/DataTree/RunClass.m +++ b/DataTree/RunClass.m @@ -520,6 +520,28 @@ function InitMlVis(obj, iBlk) + % ---------------------------------------------------------------------------------- + function mlAct = GetActiveChannels(obj) + % Load to memory if needed + err = -1; + if obj.procStream.output.IsEmpty() + obj.Load(); + err = 0; + end + + mlAct = obj.GetVar('mlActAuto'); + if ~isempty(mlAct) + mlAct = mlAct{1}; + end + + % Free memory + if err==0 + obj.FreeMemory(); + end + end + + + % ---------------------------------------------------------------------------------- function SetStims_MatInput(obj, s, t, CondNames) obj.procStream.SetStims_MatInput(s, t, CondNames); diff --git a/DataTree/TreeNodeClass.m b/DataTree/TreeNodeClass.m index 58c42293..00cbb882 100644 --- a/DataTree/TreeNodeClass.m +++ b/DataTree/TreeNodeClass.m @@ -762,6 +762,24 @@ function RenameCondition(obj, oldname, newname) + % ---------------------------------------------------------------------------------- + function mlAct = GetActiveChannels(obj) + mlAct = []; + for ii = 1:length(obj.children) + m = obj.children(1).GetActiveChannels(); + if isempty(m) + continue; + end + if isempty(mlAct) + mlAct = m; + else + mlAct(:,3) = mlAct(:,3) | m(:,3); + end + end + end + + + % ---------------------------------------------------------------------------------- function SetMeasListActMan(obj, ml) if ~exist('ml','var') diff --git a/DataTree/Version.txt b/DataTree/Version.txt index 7455fe00..dc1de315 100644 --- a/DataTree/Version.txt +++ b/DataTree/Version.txt @@ -1 +1 @@ -1.4.0 +1.6.0 diff --git a/FuncRegistry/UserFunctions/iWLS/ar_fit.m b/FuncRegistry/UserFunctions/iWLS/ar_fit.m index 758b8d9a..e66d9371 100644 --- a/FuncRegistry/UserFunctions/iWLS/ar_fit.m +++ b/FuncRegistry/UserFunctions/iWLS/ar_fit.m @@ -26,7 +26,10 @@ coef = invR * Q'*yy(lstValid); res = yy(lstValid) - X(lstValid,:)*coef; else - [coef, res] = stepwise(X(lstValid,:), yy(lstValid)); + [B,SE,PVAL,in,stats,nextstep,history] = stepwisefit(X(lstValid,:), yy(lstValid),'Display','off'); + coef = B; + res = stats.yr; +% [coef, res] = stepwisefit(X(lstValid,:), yy(lstValid)); end res = res(1:n); yhat = y - res; diff --git a/FuncRegistry/UserFunctions/iWLS/ar_glm_final.m b/FuncRegistry/UserFunctions/iWLS/ar_glm_final.m index 65cec131..1f5d1636 100644 --- a/FuncRegistry/UserFunctions/iWLS/ar_glm_final.m +++ b/FuncRegistry/UserFunctions/iWLS/ar_glm_final.m @@ -22,7 +22,7 @@ res = y-X*B; - Pmax = 100; + Pmax = 10; a = robust_ar_fit(res, Pmax); f = [1; -a(2:end)]; diff --git a/FuncRegistry/Version.txt b/FuncRegistry/Version.txt index 0698a2ca..bf6d67a5 100644 --- a/FuncRegistry/Version.txt +++ b/FuncRegistry/Version.txt @@ -1 +1 @@ -1.1.0 +1.2.0 diff --git a/Utils/Shared/Version.txt b/Utils/Shared/Version.txt index 59351ec0..977221bd 100644 --- a/Utils/Shared/Version.txt +++ b/Utils/Shared/Version.txt @@ -1,2 +1 @@ -1.1.1 - +1.1.2 diff --git a/Utils/Shared/checkSharedLibCompatibility.m b/Utils/Shared/checkSharedLibCompatibility.m new file mode 100644 index 00000000..1c328146 --- /dev/null +++ b/Utils/Shared/checkSharedLibCompatibility.m @@ -0,0 +1,29 @@ +function v = checkSharedLibCompatibility() +v = {}; +libs = parseGitSubmodulesFile(); +searchpaths = path(); +if ismac() + delimiter = ':'; +else + delimiter = ';'; +end +searchpaths = str2cell(searchpaths, delimiter); +for jj = 1:size(libs,1) + kk = 2; + appnamePath = libs{jj,1}; + [~, appname] = fileparts(appnamePath); + v{jj,1} = appname; + for ii = 1:length(searchpaths) + if ~isempty(strfind(searchpaths{ii}, appname)) + if ispathvalid([filesepStandard(searchpaths{ii}), 'Version.txt'], 'file') + pname = fileparts(filesepStandard(searchpaths{ii})); + v{jj,kk} = filesepStandard(searchpaths{ii}); + v{jj,kk+1} = getVernum(appname, pname); + v{jj,kk+2} = versionstr2num(getVernum(appname, pname)); + fprintf('Found %s, v(%s) in %s\n', appname, v{jj,kk}, filesepStandard(searchpaths{ii})); + kk = kk+3; + end + end + end +end + diff --git a/Utils/Shared/versionstr2num.m b/Utils/Shared/versionstr2num.m new file mode 100644 index 00000000..94668581 --- /dev/null +++ b/Utils/Shared/versionstr2num.m @@ -0,0 +1,9 @@ +function n = versionstr2num(s) +n = 0; +c = str2cell(s,'.'); +m = length(c); +b = 100; +for ii = 1:length(c) + n = n + (str2num(c{ii}) * b^(m-ii)); +end + diff --git a/Version.txt b/Version.txt index 534223aa..45382aab 100644 --- a/Version.txt +++ b/Version.txt @@ -1,2 +1,3 @@ -1.71.1 +1.72.0 + From e73d6502931188870e893ad2c9b0ca7c7e10e807 Mon Sep 17 00:00:00 2001 From: jayd1860 Date: Wed, 12 Apr 2023 10:58:27 -0400 Subject: [PATCH 2/3] v1.76.0 - Merge squashed commit from development branch of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 16805194baa9a8051f3b8b2097d96bebbf8d41e9 Author: jayd1860 Date: Tue Apr 11 14:49:33 2023 -0400 -- Sync setpaths in DataTree with DataTree library commit 0f5428e741e669b0c902737ce71e30958dce6549 Author: jayd1860 <44243610+jayd1860@users.noreply.github.com> Date: Tue Apr 4 16:30:29 2023 -0400 v1.76.0 -- Update DataTree shared lib submodule (#168) * v1.75.4 * DataTree, v1.11.1 -- Fix error when loading some probes in NirsClass check that NirsClass.GetChannelsMeanDistance() is error checking size of SrcPos3D and DetPos3D against meas list indices. * Utils, v1.4.4 -- Change font size in MenuBox. -- Adjust button width to slightly wider. * v1.75.5 -- Fix font size for MAC for MenuBox.m and configSettingsGUI.m * v1.75.6 -- Utils, 1.4.6 -- Tweak size of MenuBox text gap. * v1.76.0 -- Update DataTree shared lib submodule * DataTree, 1.12.0 -- Change AcqDataClass GetSrcPos and GetDetPos methods to ignore '2D' and instead automatically try to retrieve 3D optodes if available otherwise it retrieves 2D. This fixes the problem of retrieving non existent or artificially generated 2D optodes that do not agree with the 'LengthUnit' which is a problem when doing image reconstruction and using a non-zero SD separation to exclude channels (possibbly end up exluding ALL chaqnnels if units apply to the wrong optodes). -- Update NirsClass so SDgui matches GrommetRot being changed back to cell array type instead of numeric array. commit 4974adb8ee3a4280c84b547b19f20d2381ba6127 Author: Meryem Ayşe Yücel <49535526+mayucel@users.noreply.github.com> Date: Mon Apr 3 12:02:43 2023 -0400 Dev may2 (#167) * Update ar_fit.m Fixing bug in filename stepwise->stepwisefit, and the relevant extracted output. * Update ar_glm_final.m Reducing the number of max iterations to 10 for the sake of computational time. * Update ar_glm_final.m fix Pmax 10->100 * AR p order Order was hardcoded, changed it be 2 X sampling frequency. commit d0fac4b22d1069eaa5cba268a225eb117ddcdca1 Author: jayd1860 <44243610+jayd1860@users.noreply.github.com> Date: Thu Mar 23 02:10:45 2023 -0400 v1.75.6 -- Fix font size in MenuBox on MAC (#166) * v1.75.4 * DataTree, v1.11.1 -- Fix error when loading some probes in NirsClass check that NirsClass.GetChannelsMeanDistance() is error checking size of SrcPos3D and DetPos3D against meas list indices. * Utils, v1.4.4 -- Change font size in MenuBox. -- Adjust button width to slightly wider. * v1.75.5 -- Fix font size for MAC for MenuBox.m and configSettingsGUI.m * v1.75.6 -- Utils, 1.4.6 -- Tweak size of MenuBox text gap. commit f5836455b2befcfdf237a41d06a18495f0aca4a2 Author: jayd1860 Date: Tue Mar 21 21:52:42 2023 -0400 v1.75.3 -- Fix issue with setpaths error on MAC when setting file persmissions and logging it global logger not declared. -- Clean up addremove argument to setpaths and add option (addremove=2) to exclusively load only the current repo while excluding search paths for all other similar repos commit d2c38737cf4fd47db9634dc36d84630145190ec8 Author: jayd1860 Date: Tue Mar 21 15:31:33 2023 -0400 v1.75.2 -- Sync with shared libraries (submodules). * DataTree, v1.11.0 -- Change NirsClass.ProbeEqual() to consider measurement lists in different orders but same set of SD pairs to be equal. SO SDgui doesn't think edits were made when they weren't. -- Add NirsClass.GetChannelsMeanDistance() method same as in SnirfClass * Utils, v1.4.2 -- Make another tweak in MenuBox - character size doesn't quite equal character units so we compensate by multiplying by scaling factor for button width and height. * Utils, v1.4.1 -- Fix some size and position issues with radiobutton style MenuBox. Another attempt to simplify size and position calculations. commit a44aeede7dd7c8880a2159319e7956d4e1b0f260 Author: sreekanthkura7 Date: Tue Mar 21 14:25:14 2023 -0400 Add plotProbe2 display commit 27a642ee69c4801a3eda366b99fd4ca6298d95e1 Author: jayd1860 <44243610+jayd1860@users.noreply.github.com> Date: Sat Mar 18 00:49:19 2023 -0400 Fix SpatialUnit scaling in SD NirsClass (#165) * v1.75.0 -- In syncSubmodules.m add option to ignore last revision dates of the parent repo and submodules and instead force the direction of copying changes from parent repo to submodules ('parent2submodules') or submodules to parent repo ('submodules2parent'). * DataTree, v1.9.0, v1.8.3 -- Fix wrong probe scaling converting units in SnirfClass.ProbeClass and NirsClass -- Add m (meter) to SpatialUnits NirsClass add appropriate scaling. When changing units in SDgui ask iof fixing incorrect units or simply changing units and associated coordinates. * Utils, v1.4.0, v1.3.5 -- Remove non-class method convert_optodepos_to_circlular_2D_pos.m from ProbeClass to Utils. -- Add missing function pretty_print_struct.m used in DataTree to Utils library -- Fix bug in last commit having to do with the MenuBox radiobutton dialog selction of spatial unit change: either a) correcting them and changing units ONLY or b) changing units and coordinates. -- Add radiobutton selection style option to MenuBox.m -- Fix some issues with MenuBox.m including issue with MenuBox disappearing outside of screen when run standalone with no parent GUI. * v1.75.1 -- DataTree, v1.10.0, Fix error in probe registration loading SD file where anchor points are not copied correctly. commit 4544f60fa262dbc8afb91fd96c3e802a599dcc77 Author: jayd1860 Date: Wed Mar 8 13:23:12 2023 -0500 v1.74.3 -- Sync with latest shared libraries * DataTree, v1.8.1 -- Fix error when trying to retrieve ppf param during image recon because dataTree.currElem.procStream.fcalls being empty. This is because of using GroupClass.CompareVersions caused procstream fcalls from groupResults not to load (it was deemed too old) which is clearly not what we want. Removed for now the comparison (CompareVersions for now always returns 0) until we can figure out best way to handle old versions of groupResults. -- Provide ProbeClass.GetScaleFactor() method to retrieve private scaling field -- In ExportProcStreamDependencies get rid of the *_Library_Version suffix next to each library name in the JSON output * FuncRegistry, v1.2.2 -- Fix issue with converting mlACt vector to 2D array with mlAct_Initialize.m during image recon. * Utils, v1.3.2 -- Add ability to versionstr2num to process version arguments in the form of 3 numbers rather than a string for backward compatability with DataTree GroupClass to get old style version. commit 104e17ca2c8c423fe294c73a098b9e741bf55a86 Author: jayd1860 Date: Tue Mar 7 11:53:01 2023 -0500 v1.74.1 -- Fix Logger.Write issue when getting cell array of strings input. -- Remove redundant copy of jsonlab library which has been moved to Utils shared library files from jsonlab library. -- Utils, v1.3.1: Remove unnecessary files from jsonlab library. License looks like it allows modification as long as you include the license itself in the distribution. -- FuncRegistry, v1.2.1: Update version number after Meryem's changes to ar_glm_final.m. commit 349a96d64cf2f41c51b390d910de2b5fcee36648 Author: jayd1860 Date: Sun Mar 5 14:41:52 2023 -0500 v1.74.0 -- Move updateVersions and supporting function out of Utils/submodules to Utils/Shared in DataTree standalone mode to update repo versions. -- Move JSON library from Utils to Utils/Shared for use in DataTree standalone mode to write JSON files when expoerting proc stream. * DataTree, v1.8.0 -- Fix issue of DataTree in standalone mode not getting the right value for config parameter "Export Processing Stream Functions". -- Remove reliance on static method TreeNodeClass.ExportProcStreamFunctions to get value of config parameter which tells you if code should export processing stream. Instead switch to getting the value directly from the ConfigFileClass global variable cfg which is automatically updated when changes are made to any config parameters through the configSettingsGUI. * Utils, v1.3.0 -- Add JSON library so that export of procesaswing stream to JSON files can work in DataTree in standalone mode. -- Add repoManagement/updateVersions.m and supporting function so that use can use the script to update versions in DataTree in standalone mode. -- When saving config parameters in configSettingsGUI update global ConfigFileClass variable cfg so that DataTree in standalone mode has immediate access to changes in config params without having to rerun dataTree. commit d86615dafde860e4ccd53dff1b7927feae63e199 Author: Meryem Ayşe Yücel <49535526+mayucel@users.noreply.github.com> Date: Sun Mar 5 14:35:03 2023 -0500 Dev may2 (#163) * Update ar_fit.m Fixing bug in filename stepwise->stepwisefit, and the relevant extracted output. * Update ar_glm_final.m Reducing the number of max iterations to 10 for the sake of computational time. * Update ar_glm_final.m fix Pmax 10->100 * AR p order Order was hardcoded, changed it be 2 X sampling frequency. --------- Co-authored-by: jayd1860 <44243610+jayd1860@users.noreply.github.com> commit c3a2f13b154dc409a2e50fd55a08e5cb14a79fae Author: jayd1860 Date: Sat Mar 4 23:04:36 2023 -0500 v1.73.1 -- Fix issue where you get error in getVernum.m when namespace isn't set. commit 222f6e09354b2175f3177760575cc031f588ee68 Author: jayd1860 Date: Sat Mar 4 00:57:24 2023 -0500 v1.73.0 -- Separate ApplicationName into ApplicationName and Version when exporting processing stream to JSON file. -- Change default for config parameter Export Processing Stream Functions from No to Yes. -- Add library version numbers to JSON file generated by export processing stream utility -- Add utility updateVersions.m to Utils/submodules to automatically generate new version numbers for main application and shared library depedencies -- Fix issue with copying probe in NirsClass.CopyProbe when concerting from .nirs to .snirf. Also use NirsClass constructor rather than load() to load .nirs data when converting from .nirs to .snirf. commit 16a3aae8b19eb033397afe09532a59eadec1e314 Merge: 9991e1d 40bb3ca Author: jayd1860 <44243610+jayd1860@users.noreply.github.com> Date: Fri Mar 3 00:46:55 2023 -0500 Merge branch 'master' into development commit 9991e1d53f66016b97fe8fbcb111823923d97f1c Author: jayd1860 Date: Fri Mar 3 00:28:17 2023 -0500 -- Update version numbers for DataTree and FuncRegistry after new fixes commit 592a42fe7edcf77462dfcfe7457328606293625e Author: Meryem Ayşe Yücel <49535526+mayucel@users.noreply.github.com> Date: Fri Mar 3 00:23:21 2023 -0500 Update ar_fit.m (#161) * Update ar_fit.m Fixing bug in filename stepwise->stepwisefit, and the relevant extracted output. * Update ar_glm_final.m Reducing the number of max iterations to 10 for the sake of computational time. commit a383a3f3ce5d861f3dd811e69ef69c20bad6f2dd Author: jayd1860 Date: Thu Mar 2 23:43:34 2023 -0500 v1.72.0 -- Fix error when exporting processing stream to JSON files because of bug in extracting mlActMan - since it is no longer a vector but a 2D array of sd pairs plus data type. commit bf948c019836497f2146ee1c844946ace3f2097a Author: jayd1860 Date: Tue Feb 28 23:28:38 2023 -0500 # Conflicts: # DataTree/AcquiredData/Nirs/NirsClass.m # DataTree/AcquiredData/Snirf/ProbeClass.m # DataTree/GroupClass.m # DataTree/Version.txt # FuncRegistry/UserFunctions/iWLS/ar_glm_final.m # FuncRegistry/Version.txt # Utils/Shared/Version.txt # Utils/Shared/versionstr2num.m # Version.txt --- .../AcquiredData/DataFiles/DataFilesClass.m | 2 +- DataTree/AcquiredData/DataFiles/Nirs2Snirf.m | 2 +- DataTree/AcquiredData/Nirs/NirsClass.m | 201 ++- DataTree/AcquiredData/Snirf/DataClass.m | 34 +- .../AcquiredData/Snirf/MetaDataTagsClass.m | 10 + DataTree/AcquiredData/Snirf/ProbeClass.m | 52 +- DataTree/AcquiredData/Snirf/SnirfClass.m | 73 + DataTree/AppSettings.cfg | 2 +- DataTree/DataTreeClass.m | 3 +- DataTree/GroupClass.m | 102 +- DataTree/ProcStream/ProcStreamClass.m | 47 +- DataTree/TreeNodeClass.m | 24 +- DataTree/Version.txt | 2 +- DataTree/setpaths.m | 97 +- FuncRegistry/UserFunctions/hmrR_GLM.m | 3 +- .../UserFunctions/iWLS/ar_glm_final.m | 3 +- FuncRegistry/UserFunctions/mlAct_Initialize.m | 10 +- FuncRegistry/Version.txt | 2 +- MainGUI/MainGUI.fig | Bin 100572 -> 101114 bytes MainGUI/MainGUI.m | 61 +- PlotProbe2GUI/PlotProbe2.fig | Bin 0 -> 52297 bytes PlotProbe2GUI/PlotProbe2.m | 826 ++++++++++ PlotProbe2GUI/README.md | 14 + Utils/Shared/Logger.m | 28 +- Utils/Shared/MenuBox.m | 269 ++- Utils/Shared/Version.txt | 2 +- Utils/Shared/configSettingsGUI.m | 29 +- .../convert_optodepos_to_circlular_2D_pos.m | 14 + Utils/Shared/dependencies.m | 12 + Utils/Shared/getCurrTime.m | 4 + Utils/Shared/getVernum.m | 17 +- Utils/Shared/guiOutsideScreenBorders.m | 7 + Utils/{ => Shared}/jsonlab/AUTHORS.txt | 0 Utils/{ => Shared}/jsonlab/ChangeLog.txt | 0 Utils/{ => Shared}/jsonlab/Contents.m | 0 Utils/{ => Shared}/jsonlab/LICENSE_BSD.txt | 0 Utils/{ => Shared}/jsonlab/LICENSE_GPLv3.txt | 0 Utils/{ => Shared}/jsonlab/README.txt | 0 Utils/{ => Shared}/jsonlab/base64decode.m | 0 Utils/{ => Shared}/jsonlab/base64encode.m | 0 Utils/{ => Shared}/jsonlab/decodevarname.m | 0 Utils/{ => Shared}/jsonlab/encodevarname.m | 0 .../{ => Shared}/jsonlab/fast_match_bracket.m | 0 Utils/{ => Shared}/jsonlab/filterjsonmmap.m | 0 Utils/{ => Shared}/jsonlab/getfromjsonpath.m | 0 Utils/{ => Shared}/jsonlab/gzipdecode.m | 0 Utils/{ => Shared}/jsonlab/gzipencode.m | 0 Utils/{ => Shared}/jsonlab/isoctavemesh.m | 0 Utils/{ => Shared}/jsonlab/jdatadecode.m | 0 Utils/{ => Shared}/jsonlab/jdataencode.m | 0 Utils/{ => Shared}/jsonlab/jload.m | 0 Utils/{ => Shared}/jsonlab/jsave.m | 0 Utils/{ => Shared}/jsonlab/jsonget.m | 0 Utils/{ => Shared}/jsonlab/jsonopt.m | 0 Utils/{ => Shared}/jsonlab/jsonset.m | 0 Utils/{ => Shared}/jsonlab/loadbj.m | 0 Utils/{ => Shared}/jsonlab/loadjd.m | 0 Utils/{ => Shared}/jsonlab/loadjson.m | 0 Utils/{ => Shared}/jsonlab/loadmsgpack.m | 0 Utils/{ => Shared}/jsonlab/loadubjson.m | 0 Utils/{ => Shared}/jsonlab/lz4decode.m | 0 Utils/{ => Shared}/jsonlab/lz4encode.m | 0 Utils/{ => Shared}/jsonlab/lz4hcdecode.m | 0 Utils/{ => Shared}/jsonlab/lz4hcencode.m | 0 Utils/{ => Shared}/jsonlab/lzipdecode.m | 0 Utils/{ => Shared}/jsonlab/lzipencode.m | 0 Utils/{ => Shared}/jsonlab/lzmadecode.m | 0 Utils/{ => Shared}/jsonlab/lzmaencode.m | 0 Utils/{ => Shared}/jsonlab/match_bracket.m | 0 Utils/{ => Shared}/jsonlab/mergestruct.m | 0 Utils/{ => Shared}/jsonlab/nestbracket2dim.m | 0 Utils/{ => Shared}/jsonlab/savebj.m | 0 Utils/{ => Shared}/jsonlab/savejd.m | 0 Utils/{ => Shared}/jsonlab/savejson.m | 0 Utils/{ => Shared}/jsonlab/savemsgpack.m | 0 Utils/{ => Shared}/jsonlab/saveubjson.m | 0 Utils/{ => Shared}/jsonlab/varargin2struct.m | 0 Utils/{ => Shared}/jsonlab/zlibdecode.m | 0 Utils/{ => Shared}/jsonlab/zlibencode.m | 0 Utils/Shared/pretty_print_matrix.m | 31 + Utils/Shared/pretty_print_struct.m | 45 + .../repoManagement}/exeShellCmds.m | 2 +- .../repoManagement}/gitRevert.m | 11 +- .../repoManagement}/gitStatus.m | 11 +- Utils/Shared/repoManagement/hasChanges.m | 33 + .../Shared/repoManagement/incrementVersion.m | 30 + Utils/Shared/repoManagement/updateVersions.m | 126 ++ Utils/Shared/versionnum2str.m | 10 + Utils/Shared/versionstr2num.m | 14 +- Utils/jsonlab/DESCRIPTION | 13 - Utils/jsonlab/INDEX | 51 - Utils/jsonlab/README.rst | 718 -------- Utils/jsonlab/examples/demo_jsonlab_basic.m | 413 ----- Utils/jsonlab/examples/demo_msgpack_basic.m | 353 ---- Utils/jsonlab/examples/demo_ubjson_basic.m | 367 ----- Utils/jsonlab/examples/example1.json | 33 - Utils/jsonlab/examples/example2.json | 22 - Utils/jsonlab/examples/example3.json | 11 - Utils/jsonlab/examples/example4.json | 35 - .../jsonlab/examples/jsonlab_basictest.matlab | 1468 ----------------- Utils/jsonlab/examples/jsonlab_selftest.m | 27 - .../jsonlab/examples/jsonlab_selftest.matlab | 163 -- Utils/jsonlab/examples/jsonlab_speedtest.m | 21 - .../examples/jsonlab_ubjson_basictest.matlab | 937 ----------- Utils/jsonlab/examples/random_json_joke.m | 29 - Utils/jsonlab/gendocs.sh | 69 - Utils/jsonlab/genlog.sh | 3 - Utils/jsonlab/images/jsonlab-banner.png | Bin 7350 -> 0 bytes Utils/jsonlab/images/jsonlab-logo.png | Bin 2906 -> 0 bytes Utils/jsonlab/jsonlab.prj | 175 -- Utils/jsonlab/package.json | 21 - Utils/jsonlab/test/run_jsonlab_test.m | 345 ---- Utils/jsonlab/test/test_jsonlab.m | 32 - Utils/submodules/cleanupSharedLibs.m | 17 - Utils/submodules/downloadSharedLibs.m | 175 -- .../submodules/downloadSubmodulesWithoutGit.m | 74 - Utils/submodules/gitSubmodulesInit.m | 88 - Utils/submodules/gitSubmodulesUpdate.m | 28 - Utils/submodules/resetSubmodules.m | 17 - Utils/submodules/syncSubmodules.m | 63 +- Utils/submodules/updateSubmodulesWithoutGit.m | 51 - Version.txt | 4 +- setpaths.m | 35 +- 123 files changed, 1923 insertions(+), 6200 deletions(-) create mode 100644 PlotProbe2GUI/PlotProbe2.fig create mode 100644 PlotProbe2GUI/PlotProbe2.m create mode 100644 PlotProbe2GUI/README.md create mode 100644 Utils/Shared/convert_optodepos_to_circlular_2D_pos.m create mode 100644 Utils/Shared/dependencies.m create mode 100644 Utils/Shared/getCurrTime.m rename Utils/{ => Shared}/jsonlab/AUTHORS.txt (100%) rename Utils/{ => Shared}/jsonlab/ChangeLog.txt (100%) rename Utils/{ => Shared}/jsonlab/Contents.m (100%) rename Utils/{ => Shared}/jsonlab/LICENSE_BSD.txt (100%) rename Utils/{ => Shared}/jsonlab/LICENSE_GPLv3.txt (100%) rename Utils/{ => Shared}/jsonlab/README.txt (100%) rename Utils/{ => Shared}/jsonlab/base64decode.m (100%) rename Utils/{ => Shared}/jsonlab/base64encode.m (100%) rename Utils/{ => Shared}/jsonlab/decodevarname.m (100%) rename Utils/{ => Shared}/jsonlab/encodevarname.m (100%) rename Utils/{ => Shared}/jsonlab/fast_match_bracket.m (100%) rename Utils/{ => Shared}/jsonlab/filterjsonmmap.m (100%) rename Utils/{ => Shared}/jsonlab/getfromjsonpath.m (100%) rename Utils/{ => Shared}/jsonlab/gzipdecode.m (100%) rename Utils/{ => Shared}/jsonlab/gzipencode.m (100%) rename Utils/{ => Shared}/jsonlab/isoctavemesh.m (100%) rename Utils/{ => Shared}/jsonlab/jdatadecode.m (100%) rename Utils/{ => Shared}/jsonlab/jdataencode.m (100%) rename Utils/{ => Shared}/jsonlab/jload.m (100%) rename Utils/{ => Shared}/jsonlab/jsave.m (100%) rename Utils/{ => Shared}/jsonlab/jsonget.m (100%) rename Utils/{ => Shared}/jsonlab/jsonopt.m (100%) rename Utils/{ => Shared}/jsonlab/jsonset.m (100%) rename Utils/{ => Shared}/jsonlab/loadbj.m (100%) rename Utils/{ => Shared}/jsonlab/loadjd.m (100%) rename Utils/{ => Shared}/jsonlab/loadjson.m (100%) rename Utils/{ => Shared}/jsonlab/loadmsgpack.m (100%) rename Utils/{ => Shared}/jsonlab/loadubjson.m (100%) rename Utils/{ => Shared}/jsonlab/lz4decode.m (100%) rename Utils/{ => Shared}/jsonlab/lz4encode.m (100%) rename Utils/{ => Shared}/jsonlab/lz4hcdecode.m (100%) rename Utils/{ => Shared}/jsonlab/lz4hcencode.m (100%) rename Utils/{ => Shared}/jsonlab/lzipdecode.m (100%) rename Utils/{ => Shared}/jsonlab/lzipencode.m (100%) rename Utils/{ => Shared}/jsonlab/lzmadecode.m (100%) rename Utils/{ => Shared}/jsonlab/lzmaencode.m (100%) rename Utils/{ => Shared}/jsonlab/match_bracket.m (100%) rename Utils/{ => Shared}/jsonlab/mergestruct.m (100%) rename Utils/{ => Shared}/jsonlab/nestbracket2dim.m (100%) rename Utils/{ => Shared}/jsonlab/savebj.m (100%) rename Utils/{ => Shared}/jsonlab/savejd.m (100%) rename Utils/{ => Shared}/jsonlab/savejson.m (100%) rename Utils/{ => Shared}/jsonlab/savemsgpack.m (100%) rename Utils/{ => Shared}/jsonlab/saveubjson.m (100%) rename Utils/{ => Shared}/jsonlab/varargin2struct.m (100%) rename Utils/{ => Shared}/jsonlab/zlibdecode.m (100%) rename Utils/{ => Shared}/jsonlab/zlibencode.m (100%) create mode 100644 Utils/Shared/pretty_print_matrix.m create mode 100644 Utils/Shared/pretty_print_struct.m rename Utils/{submodules => Shared/repoManagement}/exeShellCmds.m (94%) rename Utils/{submodules => Shared/repoManagement}/gitRevert.m (57%) rename Utils/{submodules => Shared/repoManagement}/gitStatus.m (89%) create mode 100644 Utils/Shared/repoManagement/hasChanges.m create mode 100644 Utils/Shared/repoManagement/incrementVersion.m create mode 100644 Utils/Shared/repoManagement/updateVersions.m create mode 100644 Utils/Shared/versionnum2str.m delete mode 100644 Utils/jsonlab/DESCRIPTION delete mode 100644 Utils/jsonlab/INDEX delete mode 100644 Utils/jsonlab/README.rst delete mode 100644 Utils/jsonlab/examples/demo_jsonlab_basic.m delete mode 100644 Utils/jsonlab/examples/demo_msgpack_basic.m delete mode 100644 Utils/jsonlab/examples/demo_ubjson_basic.m delete mode 100644 Utils/jsonlab/examples/example1.json delete mode 100644 Utils/jsonlab/examples/example2.json delete mode 100644 Utils/jsonlab/examples/example3.json delete mode 100644 Utils/jsonlab/examples/example4.json delete mode 100644 Utils/jsonlab/examples/jsonlab_basictest.matlab delete mode 100644 Utils/jsonlab/examples/jsonlab_selftest.m delete mode 100644 Utils/jsonlab/examples/jsonlab_selftest.matlab delete mode 100644 Utils/jsonlab/examples/jsonlab_speedtest.m delete mode 100644 Utils/jsonlab/examples/jsonlab_ubjson_basictest.matlab delete mode 100644 Utils/jsonlab/examples/random_json_joke.m delete mode 100644 Utils/jsonlab/gendocs.sh delete mode 100644 Utils/jsonlab/genlog.sh delete mode 100644 Utils/jsonlab/images/jsonlab-banner.png delete mode 100644 Utils/jsonlab/images/jsonlab-logo.png delete mode 100644 Utils/jsonlab/jsonlab.prj delete mode 100644 Utils/jsonlab/package.json delete mode 100644 Utils/jsonlab/test/run_jsonlab_test.m delete mode 100644 Utils/jsonlab/test/test_jsonlab.m delete mode 100644 Utils/submodules/cleanupSharedLibs.m delete mode 100644 Utils/submodules/downloadSharedLibs.m delete mode 100644 Utils/submodules/downloadSubmodulesWithoutGit.m delete mode 100644 Utils/submodules/gitSubmodulesInit.m delete mode 100644 Utils/submodules/gitSubmodulesUpdate.m delete mode 100644 Utils/submodules/resetSubmodules.m delete mode 100644 Utils/submodules/updateSubmodulesWithoutGit.m diff --git a/DataTree/AcquiredData/DataFiles/DataFilesClass.m b/DataTree/AcquiredData/DataFiles/DataFilesClass.m index 7e8a98ae..39582606 100644 --- a/DataTree/AcquiredData/DataFiles/DataFilesClass.m +++ b/DataTree/AcquiredData/DataFiles/DataFilesClass.m @@ -472,7 +472,7 @@ function ErrorCheck(obj) if obj.files(ii).isdir continue; end - filename = [obj.rootdir, obj.files(ii).name]; + filename = [obj.rootdir, obj.files(ii).name]; %#ok eval( sprintf('o = %s(filename);', constructor) ); if o.GetError() < 0 obj.logger.Write('DataFilesClass.ErrorCheck - ERROR: In file "%s" %s. File will not be added to data set\n', obj.files(ii).name, o.GetErrorMsg()); diff --git a/DataTree/AcquiredData/DataFiles/Nirs2Snirf.m b/DataTree/AcquiredData/DataFiles/Nirs2Snirf.m index 453cf479..61c15c69 100644 --- a/DataTree/AcquiredData/DataFiles/Nirs2Snirf.m +++ b/DataTree/AcquiredData/DataFiles/Nirs2Snirf.m @@ -54,7 +54,7 @@ fprintf('Converting %s to %s\n', src, dst); waitbar_improved(ii/length(nirsfiles), h, sprintf('Converting %s to SNIRF: %d of %d', nirsfiles(ii).name, ii, length(nirsfiles))); - nirs = load(src,'-mat'); + nirs = NirsClass(src); if DEBUG==false snirf(ii) = SnirfClass(nirs); diff --git a/DataTree/AcquiredData/Nirs/NirsClass.m b/DataTree/AcquiredData/Nirs/NirsClass.m index 13b127d4..b77e2595 100644 --- a/DataTree/AcquiredData/Nirs/NirsClass.m +++ b/DataTree/AcquiredData/Nirs/NirsClass.m @@ -349,10 +349,23 @@ function SaveMat(obj, fname, options) fields{2} = propnames(obj2.SD); fieldsToExclude = { ... + 'MeasList'; ... 'MeasListAct'; ... 'SrcMap'; ... }; + + % Check MeasList explicitely + if (isfield(obj.SD,'MeasList') && ~isfield(obj2.SD,'MeasList')) || ~isfield(obj.SD,'MeasList') && isfield(obj2.SD,'MeasList') + return; + end + [~, k1] = sortrows(obj.SD.MeasList); + [~, k2] = sortrows(obj2.SD.MeasList); + if ~all(obj.SD.MeasList(k1,:) == obj2.SD.MeasList(k2,:)) + return; + end + + for kk = 1:length(fields) for jj = 1:length(fields{kk}) field = fields{kk}{jj}; @@ -363,7 +376,7 @@ function SaveMat(obj, fname, options) end % Now compare field - if ~isfield(obj.SD,field) || ~isfield(obj2.SD,field) + if (isfield(obj.SD,field) && ~isfield(obj2.SD,field)) || ~isfield(obj.SD,field) && isfield(obj2.SD,field) return; end if eval( sprintf('~strcmp(class(obj.SD.%s), class(obj2.SD.%s))', field, field) ) @@ -1044,56 +1057,87 @@ function RenameCondition(obj, oldname, newname) 'SrcPos3D',[], ... 'DetPos3D',[], ... 'DummyPos3D',[], ... + 'nSrcs',0,... + 'nDets',0,... + 'nDummys',0,... 'SrcGrommetType',{{}}, ... 'DetGrommetType',{{}}, ... 'DummyGrommetType',{{}}, ... - 'SrcGrommetRot',[], ... - 'DetGrommetRot',[], ... - 'DummyGrommetRot',[], ... + 'SrcGrommetRot',{{}}, ... + 'DetGrommetRot',{{}}, ... + 'DummyGrommetRot',{{}}, ... 'Landmarks',obj.InitLandmarks(), ... 'Landmarks2D',obj.InitLandmarks(), ... 'Landmarks3D',obj.InitLandmarks(), ... 'MeasList',[], ... 'MeasListAct',[], ... 'SpringList',[], ... - 'AnchorList',[], ... + 'AnchorList',{{}}, ... 'SrcMap',[], ... 'SpatialUnit','', ... 'xmin',0, ... 'xmax',0, ... 'ymin',0, ... 'ymax',0, ... - 'auxChannels',[] ... + 'auxChannels',{{}} ... ); end % ---------------------------------------------------------------------------------- - function SetProbeSpatialUnit(obj, spatialUnitNew) - scaling = 1; - if strcmpi(spatialUnitNew,'mm') - if strcmpi(obj.SD.SpatialUnit,'cm') - scaling = 10; - end - elseif strcmpi(spatialUnitNew,'cm') - if strcmpi(obj.SD.SpatialUnit,'mm') - scaling = 1/10; - end - else - spatialUnitNew = ''; + function SetProbeSpatialUnit(obj, spatialUnitNew, scaling, ndims) + if ~exist('ndims','var') + ndims = '2d'; end + + % Set scaling based on current units and desired units if they do not match AND + % scaling was not explcitly specified (i.e., passed in as an argument). + if ~exist('scaling','var') || isempty(scaling) + scaling = 1; + if strcmpi(spatialUnitNew,'mm') + if strcmpi(obj.SD.SpatialUnit,'cm') + scaling = 10; + elseif strcmpi(obj.SD.SpatialUnit,'m') + scaling = 1000; + end + elseif strcmpi(spatialUnitNew,'cm') + if strcmpi(obj.SD.SpatialUnit,'mm') + scaling = 1/10; + elseif strcmpi(obj.SD.SpatialUnit,'m') + scaling = 100; + end + elseif strcmpi(spatialUnitNew,'m') + if strcmpi(obj.SD.SpatialUnit,'mm') + scaling = 1/1000; + elseif strcmpi(obj.SD.SpatialUnit,'cm') + scaling = 1/100; + end + else + spatialUnitNew = ''; + end + end + + obj.SD.SpatialUnit = spatialUnitNew; - obj.SD.SrcPos = obj.SD.SrcPos * scaling; - obj.SD.DetPos = obj.SD.DetPos * scaling; - obj.SD.DummyPos = obj.SD.DummyPos * scaling; - if size(obj.SD.SpringList,2)==3 - lst = find(obj.SD.SpringList(:,3)~=-1); - obj.SD.SpringList(lst,3) = obj.SD.SpringList(lst,3) * scaling; + + if isempty(ndims) || strcmpi(ndims, '2D') + obj.SD.SrcPos = obj.SD.SrcPos * scaling; + obj.SD.DetPos = obj.SD.DetPos * scaling; + obj.SD.DummyPos = obj.SD.DummyPos * scaling; + if size(obj.SD.SpringList,2)==3 + lst = find(obj.SD.SpringList(:,3)~=-1); + obj.SD.SpringList(lst,3) = obj.SD.SpringList(lst,3) * scaling; + end + obj.SD.Landmarks.pos = obj.SD.Landmarks.pos * scaling; + end + + if isempty(ndims) || strcmpi(ndims, '3D') + obj.SD.SrcPos3D = obj.SD.SrcPos3D * scaling; + obj.SD.DetPos3D = obj.SD.DetPos3D * scaling; + obj.SD.DummyPos3D = obj.SD.DummyPos3D * scaling; + obj.SD.Landmarks3D.pos = obj.SD.Landmarks3D.pos * scaling; end - obj.SD.Landmarks.pos = obj.SD.Landmarks.pos * scaling; - obj.SD.Landmarks3D.pos = obj.SD.Landmarks3D.pos * scaling; - obj.SD.Landmarks2D.pos = obj.SD.Landmarks2D.pos * scaling; end @@ -1102,22 +1146,25 @@ function SetProbeSpatialUnit(obj, spatialUnitNew) function FixProbeSpatialUnit(obj) if isempty(obj.SD.SpatialUnit) q = MenuBox('Spatial units not provided in probe data. Please specify spatial units of the optode coordinates?', ... - {'mm','cm',sprintf('do not know')}); + {'mm','cm','m'}); if q==1 obj.SD.SpatialUnit = 'mm'; elseif q==2 obj.SD.SpatialUnit = 'cm'; elseif q==3 - obj.SD.SpatialUnit = ''; - end - end - if ~strcmpi(obj.SD.SpatialUnit,'mm') - q = MenuBox(sprintf('This probe uses ''%s'' units for probe coordinates. We recommend converting to ''mm'' units, to be consistent with Homer. Do you want to convert probe coordinates from %s to mm?', ... - obj.SD.SpatialUnit), {'YES','NO'}, 'upperleft'); - if q==1 - obj.SetProbeSpatialUnit('mm') + obj.SD.SpatialUnit = 'm'; end end + % We don't need to force anything on the user since homer and AV do internal conversions to 'mm' + % + % if ~strcmpi(obj.SD.SpatialUnit,'mm') + % q = MenuBox(sprintf('This probe uses ''%s'' units for probe coordinates. We recommend converting to ''mm'' units, to be consistent with Homer. Do you want to convert probe coordinates from %s to mm?', ... + % obj.SD.SpatialUnit), {'YES','NO'}, 'upperleft'); + % if q==1 + % obj.SetProbeSpatialUnit('mm') + % end + % end + % end @@ -1162,24 +1209,28 @@ function FixProbeSpatialUnit(obj) % ---------------------------------------------------------------------------------- - function CopyProbe(obj, SD) + function CopyProbe(obj, SD) %#ok fields = propnames(obj.SD); for ii = 1:length(fields) if eval( sprintf('isfield(SD, ''%s'')', fields{ii}) ) if eval( sprintf('strcmp(class(obj.SD.%s), class(SD.%s))', fields{ii}, fields{ii}) ) - eval( sprintf('obj.SD.%s = SD.%s;', fields{ii}, fields{ii}) ); + eval( sprintf('obj.SD.%s = SD.%s;', fields{ii}, fields{ii}) ); elseif eval( sprintf('isnumeric(obj.SD.%s) && iscell(SD.%s)', fields{ii}, fields{ii}) ) if eval( sprintf('~isempty(SD.%s)', fields{ii}) ) - eval( sprintf('obj.SD.%s = cell2array(SD.%s);', fields{ii}, fields{ii}) ); - end + for kk = 1:length(eval( sprintf('SD.%s', fields{ii}) )) + if eval( sprintf('isnumeric(SD.%s{kk}) && (length(SD.%s{kk})==1)', fields{ii}, fields{ii}) ) + eval( sprintf('obj.SD.%s(kk) = SD.%s{kk};', fields{ii}, fields{ii}) ); + end + end + end elseif eval( sprintf('isscalar(obj.SD.%s) && isscalar(SD.%s)', fields{ii}, fields{ii}) ) eval( sprintf('obj.SD.%s = SD.%s;', fields{ii}, fields{ii}) ); end end end - + % Fill in any fields that don't conform to standard SD data structure - + % SrcGrommetType d1 = size(obj.SD.SrcPos,1) - length(obj.SD.SrcGrommetType); if d1 > 0 @@ -1189,7 +1240,13 @@ function CopyProbe(obj, SD) % SrcGrommetRot d2 = size(obj.SD.SrcPos,1) - length(obj.SD.SrcGrommetRot); if d2 > 0 - obj.SD.SrcGrommetRot(end+1:end+d2) = zeros(d2,1); + for ii = length(obj.SD.SrcGrommetRot)+1:length(obj.SD.SrcGrommetRot)+d2 + if iscell(obj.SD.SrcGrommetRot) + obj.SD.SrcGrommetRot{ii} = 0; + else + obj.SD.SrcGrommetRot(ii) = 0; + end + end end % DetGrommetType @@ -1201,7 +1258,13 @@ function CopyProbe(obj, SD) % DetGrommetRot d2 = size(obj.SD.DetPos,1) - length(obj.SD.DetGrommetRot); if d2 > 0 - obj.SD.DetGrommetRot(end+1:end+d2) = zeros(d2,1); + for ii = length(obj.SD.SrcGrommetRot)+1:length(obj.SD.SrcGrommetRot)+d2 + if iscell(obj.SD.DetGrommetRot) + obj.SD.DetGrommetRot{ii} = 0; + else + obj.SD.DetGrommetRot(ii) = 0; + end + end end % DummyGrommetType @@ -1213,7 +1276,13 @@ function CopyProbe(obj, SD) % DummyGrommetRot d2 = size(obj.SD.DummyPos,1) - length(obj.SD.DummyGrommetRot); if d2 > 0 - obj.SD.DummyGrommetRot(end+1:end+d2) = zeros(d2,1); + for ii = length(obj.SD.DummyGrommetRot)+1:length(obj.SD.DummyGrommetRot)+d2 + if iscell(obj.SD.DummyGrommetRot) + obj.SD.DummyGrommetRot{ii} = 0; + else + obj.SD.DummyGrommetRot(ii) = 0; + end + end end % MesListAct @@ -1224,8 +1293,11 @@ function CopyProbe(obj, SD) elseif d>1 obj.SD.MeasListAct(end-d:end) = []; end - end - + end + + obj.SD.nSrcs = size(obj.SD.SrcPos,1); + obj.SD.nDets = size(obj.SD.DetPos,1); + obj.SD.nDummys = size(obj.SD.DummyPos,1); end @@ -1238,6 +1310,7 @@ function CopyStim(obj, obj2) end + % ---------------------------------------------------------------------------------- function CopyStruct(obj, s) fields = propnames(obj); @@ -1272,6 +1345,7 @@ function ConvertSnirfProbe(obj, snirf) end + % ---------------------------------------------------------------------------------- function ConvertSnirfData(obj, snirf) obj.d = snirf.data(1).dataTimeSeries; @@ -1279,6 +1353,7 @@ function ConvertSnirfData(obj, snirf) end + % ---------------------------------------------------------------------------------- function ConvertSnirfStim(obj, snirf) obj.s = zeros(length(obj.t), length(snirf.stim)); @@ -1302,6 +1377,7 @@ function ConvertSnirfStim(obj, snirf) end + % ---------------------------------------------------------------------------------- function ConvertSnirfAux(obj, snirf) obj.aux = zeros(length(obj.t), length(snirf.aux)); @@ -1311,6 +1387,7 @@ function ConvertSnirfAux(obj, snirf) end + % ---------------------------------------------------------------------------------- function ConvertSnirf(obj, snirf) obj.ConvertSnirfProbe(snirf); @@ -1322,7 +1399,33 @@ function ConvertSnirf(obj, snirf) - % ---------------------------------------------------------------------------------- + % ----------------------------------------------------------------------- + function [md2d, md3d] = GetChannelsMeanDistance(obj) + md2d = []; + md3d = []; + ml = obj.SD.MeasList; + if isempty(ml) + return + end + k = find(ml(:,4)==1); + ml = ml(k,:); + d1 = zeros(size(ml,1),1); + d2 = zeros(size(ml,1),1); + for ii = 1:size(ml,1) + if ml(ii,1) <= size(obj.SD.SrcPos,1) && ml(ii,2) <= size(obj.SD.DetPos,1) + d1(ii) = dist3(obj.SD.SrcPos(ml(ii,1),:), obj.SD.DetPos(ml(ii,2),:)); + end + if ml(ii,1) <= size(obj.SD.SrcPos3D,1) && ml(ii,2) <= size(obj.SD.DetPos3D,1) + d2(ii) = dist3(obj.SD.SrcPos3D(ml(ii,1),:), obj.SD.DetPos3D(ml(ii,2),:)); + end + end + md2d = mean(d1); + md3d = mean(d2); + end + + + + % ----------------------------------------------------------------------- function ErrorCheck(obj) if isempty(obj) return @@ -1368,6 +1471,7 @@ function ErrorCheck(obj) end + % ---------------------------------------------------------------- function [str, fields] = Properties2String(obj) str = ''; @@ -1382,6 +1486,7 @@ function ErrorCheck(obj) end + % ------------------------------------------------------- function changes = StimChangesMade(obj) % Load stims from file diff --git a/DataTree/AcquiredData/Snirf/DataClass.m b/DataTree/AcquiredData/Snirf/DataClass.m index 542ea28c..c431801c 100644 --- a/DataTree/AcquiredData/Snirf/DataClass.m +++ b/DataTree/AcquiredData/Snirf/DataClass.m @@ -1040,8 +1040,29 @@ function TruncateTpts(obj, n) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% methods + + % ---------------------------------------------------------------------------------- function Copy(obj, obj2) + if isempty(obj) + obj = DataClass(); + end + if isempty(obj2) + obj = DataClass(); + return; + end + if ~isa(obj2, 'DataClass') + return; + end + obj.CopyMeasurementList(obj2); + obj.dataTimeSeries = obj2.dataTimeSeries; + obj.time = obj2.time; + end + + + + % ---------------------------------------------------------------------------------- + function CopyMeasurementList(obj, obj2) if isempty(obj) obj = DataClass(); end @@ -1055,11 +1076,10 @@ function Copy(obj, obj2) for ii=1:length(obj2.measurementList) obj.measurementList(ii) = obj2.measurementList(ii).copy(); % shallow copy ok because MeasListClass has no handle properties end - obj.dataTimeSeries = obj2.dataTimeSeries; - obj.time = obj2.time; end + % ------------------------------------------------------- function B = eq(obj, obj2) B = false; @@ -1081,6 +1101,16 @@ function Copy(obj, obj2) if length(obj.measurementList)~=length(obj2.measurementList) return; end + if ~obj.EqualMeasurementLists(obj2) + return; + end + B = true; + end + + + % ------------------------------------------------------- + function B = EqualMeasurementLists(obj, obj2) + B = false; for ii=1:length(obj.measurementList) if obj.measurementList(ii)~=obj2.measurementList(ii) return; diff --git a/DataTree/AcquiredData/Snirf/MetaDataTagsClass.m b/DataTree/AcquiredData/Snirf/MetaDataTagsClass.m index 3a855ab5..2306b2b8 100644 --- a/DataTree/AcquiredData/Snirf/MetaDataTagsClass.m +++ b/DataTree/AcquiredData/Snirf/MetaDataTagsClass.m @@ -191,6 +191,16 @@ function SetLengthUnit(obj, unit) end + % ---------------------------------------------------------------------------------- + function val = GetLengthUnit(obj) + val = ''; + if isempty(obj) + return + end + val = obj.tags.LengthUnit; + end + + % ---------------------------------------------------------------------------------- function nbytes = MemoryRequired(obj) nbytes = 0; diff --git a/DataTree/AcquiredData/Snirf/ProbeClass.m b/DataTree/AcquiredData/Snirf/ProbeClass.m index c3c7a66d..11d9ca93 100644 --- a/DataTree/AcquiredData/Snirf/ProbeClass.m +++ b/DataTree/AcquiredData/Snirf/ProbeClass.m @@ -41,22 +41,22 @@ SD = n.SD; obj.wavelengths = SD.Lambda; obj.wavelengthsEmission = []; - if isfield(SD,'SrcPos2D') & ~isempty(SD.SrcPos2D) + if isfield(SD,'SrcPos2D') && ~isempty(SD.SrcPos2D) obj.sourcePos2D = SD.SrcPos2D; else obj.sourcePos2D = SD.SrcPos; end - if isfield(SD,'DetPos2D') & ~isempty(SD.DetPos2D) + if isfield(SD,'DetPos2D') && ~isempty(SD.DetPos2D) obj.detectorPos2D = SD.DetPos2D; else obj.detectorPos2D = SD.DetPos; end - if isfield(SD,'SrcPos3D') & ~isempty(SD.SrcPos3D) + if isfield(SD,'SrcPos3D') && ~isempty(SD.SrcPos3D) obj.sourcePos3D = SD.SrcPos3D; else obj.sourcePos3D = SD.SrcPos; end - if isfield(SD,'DetPos3D') & ~isempty(SD.DetPos3D) + if isfield(SD,'DetPos3D') && ~isempty(SD.DetPos3D) obj.detectorPos3D = SD.DetPos3D; else obj.detectorPos3D = SD.DetPos; @@ -132,19 +132,6 @@ function BackwardCompatibility(obj) end end - % ------------------------------------------------------------------------------- - function xy = convert_optodepos_to_circlular_2D_pos(obj, pos, T, norm_factor) - pos = [pos ones(size(pos,1),1)]; - pos_unit_sphere = pos*T; - pos_unit_sphere_norm = sqrt(sum(pos_unit_sphere.^2,2)); - pos_unit_sphere = pos_unit_sphere./pos_unit_sphere_norm ; - - [azimuth,elevation,r] = cart2sph(pos_unit_sphere(:,1),pos_unit_sphere(:,2),pos_unit_sphere(:,3)); - elevation = pi/2-elevation; - [x,y] = pol2cart(azimuth,elevation); % get plane coordinates - xy = [x y]; - xy = xy/norm_factor; % set maximum to unit length - end % ---------------------------------------------- @@ -161,9 +148,10 @@ function BackwardCompatibility(obj) end end + + % ------------------------------------------------------- - function Project_3D_to_2D(obj) - + function Project_3D_to_2D(obj) if isempty(obj.sourcePos2D) && isempty(obj.detectorPos2D) if isempty(obj.landmarkPos3D) || ~obj.isValidLandmarkLabels() nSource = size(obj.sourcePos3D,1); @@ -230,8 +218,8 @@ function Project_3D_to_2D(obj) obj.landmarkPos2D = refpts_2D.pos; % - obj.sourcePos2D = convert_optodepos_to_circlular_2D_pos(obj, obj.sourcePos3D, T, norm_factor); - obj.detectorPos2D = convert_optodepos_to_circlular_2D_pos(obj, obj.detectorPos3D, T, norm_factor); + obj.sourcePos2D = convert_optodepos_to_circlular_2D_pos(obj.sourcePos3D, T, norm_factor); + obj.detectorPos2D = convert_optodepos_to_circlular_2D_pos(obj.detectorPos3D, T, norm_factor); end end end @@ -259,7 +247,7 @@ function Project_3D_to_2D(obj) if exist('LengthUnit','var') % Figure out the scaling factor to multiple by to get the coorinates to be in mm units if strcmpi(LengthUnit,'m') % meter units - obj.scaling = 100; + obj.scaling = 1000; elseif strcmpi(LengthUnit,'cm') % centimeter units obj.scaling = 10; end @@ -394,11 +382,8 @@ function SaveHdf5(obj, fileobj, location) % --------------------------------------------------------- - function srcpos = GetSrcPos(obj,flag2d) - if ~exist('flag2d','var') - flag2d = 0; - end - if flag2d==0 + function srcpos = GetSrcPos(obj, ~) + if ~isempty(obj.sourcePos3D) srcpos = obj.sourcePos3D; else srcpos = obj.sourcePos2D; @@ -407,11 +392,8 @@ function SaveHdf5(obj, fileobj, location) % --------------------------------------------------------- - function detpos = GetDetPos(obj,flag2d) - if ~exist('flag2d','var') - flag2d = 0; - end - if flag2d==0 + function detpos = GetDetPos(obj, ~) + if ~isempty(obj.detectorPos3D) detpos = obj.detectorPos3D; else detpos = obj.detectorPos2D; @@ -574,6 +556,12 @@ function SaveHdf5(obj, fileobj, location) end + % --------------------------------------------------------- + function val = GetScaleFactor(obj) + val = obj.scaling; + end + + end end diff --git a/DataTree/AcquiredData/Snirf/SnirfClass.m b/DataTree/AcquiredData/Snirf/SnirfClass.m index 66ae5852..022992ee 100644 --- a/DataTree/AcquiredData/Snirf/SnirfClass.m +++ b/DataTree/AcquiredData/Snirf/SnirfClass.m @@ -1794,6 +1794,79 @@ function Info(obj) end + + + % ----------------------------------------------------------------------- + function [md2d, md3d] = GetChannelsMeanDistance(obj) + ml = obj.data(1).GetMeasListSrcDetPairs(); + d1 = zeros(size(ml,1),1); + for ii = 1:length(d1) + d1(ii) = dist3(obj.probe.sourcePos2D(ml(ii,1),:), obj.probe.detectorPos2D(ml(ii,2),:)); + d2(ii) = dist3(obj.probe.sourcePos3D(ml(ii,1),:), obj.probe.detectorPos3D(ml(ii,2),:)); + end + md2d = mean(d1); + md3d = mean(d2); + end + + + + % ----------------------------------------------------------------------- + function err = ErrorCheckSpatialUnits(obj) + err = 0; + msg = []; + [md2d, md3d] = obj.GetChannelsMeanDistance(); + LengthUnitDeclared = obj.metaDataTags.GetLengthUnit(); + magnitudeMm = log10(30); + magnitudeCm = log10(3); + magnitudeM = log10(.03); + + % 2D coordinates + diffMm = abs(magnitudeMm - log10(md2d)); + diffCm = abs(magnitudeCm - log10(md2d)); + diffM = abs(magnitudeM - log10(md2d)); + [~, idx] = min([diffMm, diffCm, diffM]); + if idx == 1 + LengthUnitActual2D = 'mm'; + elseif idx == 2 + LengthUnitActual2D = 'cm'; + elseif idx == 3 + LengthUnitActual2D = 'm'; + end + if ~strcmpi(LengthUnitDeclared, LengthUnitActual2D) + msg{1} = sprintf('WARNING: Declared LengthUnit (%s) might not match the likely actual units (%s) of the 2D coordinates\n', ... + LengthUnitDeclared, LengthUnitActual2D); + end + + % 2D coordinates + diffMm = abs(magnitudeMm - log10(md3d)); + diffCm = abs(magnitudeCm - log10(md3d)); + diffM = abs(magnitudeM - log10(md3d)); + [~, idx] = min([diffMm, diffCm, diffM]); + if idx == 1 + LengthUnitActual3D = 'mm'; + elseif idx == 2 + LengthUnitActual3D = 'cm'; + elseif idx == 3 + LengthUnitActual3D = 'm'; + end + if ~strcmpi(LengthUnitDeclared, LengthUnitActual3D) + msg{2} = sprintf('WARNING: Declared LengthUnit (%s) might not match the likely actual units (%s) of the 3D coordinates\n\n', ... + LengthUnitDeclared, LengthUnitActual3D); + end + + % Compare 2D units with 3D units + if ~strcmpi(LengthUnitActual2D, LengthUnitActual3D) + msg{3} = sprintf('WARNING: The likely actual units of the 2D coordinates (%s) might not match the like actual units of the 3D coordinates (%s)\n\n', ... + LengthUnitActual3D, LengthUnitActual3D); + end + + if ~isempty(msg) + MenuBox(msg); + end + end + + + end diff --git a/DataTree/AppSettings.cfg b/DataTree/AppSettings.cfg index 7e900b0b..3d9865be 100644 --- a/DataTree/AppSettings.cfg +++ b/DataTree/AppSettings.cfg @@ -29,7 +29,7 @@ No SNIRF % Export Processing Stream Functions # Yes, No -No +Yes % Export Stim To TSV File # Yes, Yes_Delete_Old, No No diff --git a/DataTree/DataTreeClass.m b/DataTree/DataTreeClass.m index 61020f76..fa64410e 100644 --- a/DataTree/DataTreeClass.m +++ b/DataTree/DataTreeClass.m @@ -798,9 +798,8 @@ function Save(obj, hwait) function CalcCurrElem(obj) banner = sprintf('Calculating derived data at %s with the following processing stream:\n\n', char(datetime(datetime, 'Format','HH:mm:ss, MMMM d, yyyy'))); obj.PrintProcStream(banner); - obj.currElem.ExportProcStreamFunctionsOpen(); obj.currElem.Calc(); - obj.currElem.ExportProcStreamFunctionsClose(); + obj.currElem.ExportProcStreamFunctions(); end diff --git a/DataTree/GroupClass.m b/DataTree/GroupClass.m index 3d048bcd..2b540bd9 100644 --- a/DataTree/GroupClass.m +++ b/DataTree/GroupClass.m @@ -2,7 +2,6 @@ properties % (Access = private) version; - versionStr; subjs; end @@ -27,11 +26,7 @@ obj.oldDerivedPaths = {obj.path, [obj.path, 'homerOutput']}; obj.derivedPathBidsCompliant = 'derivatives/homer'; obj.initsaveflag = false; - -% if nargin<3 || ~strcmp(varargin{3}, 'noprint') -% obj.logger.Write('Current GroupClass version %s\n', obj.GetVersionStr()); -% end - + obj.type = 'group'; obj.subjs = SubjClass().empty; @@ -72,38 +67,12 @@ % ---------------------------------------------------------------------------------- function InitVersion(obj) obj.SetVersion(getVernum('DataTree')); - obj.InitVersionStrFull(); end % ---------------------------------------------------------------------------------- function SetVersion(obj, vernum) - % Version number should be incremented whenever properties are added, deleted or changed in - % GroupClass, SubjectClass, RunClass OR acquisition class, like AcqDataClass, SnirfClass and - % its sub-classes or NirsClass - - if nargin==1 - obj.version{1} = '2'; % Major version # - obj.version{2} = '0'; % Major sub-version # - obj.version{3} = '0'; % Minor version # - obj.version{4} = '0'; % Minor sub-version # or patch #: 'p1', 'p2', etc - elseif iscell(vernum) - if ~isnumber([vernum{:}]) - return; - end - for ii=1:length(vernum) - obj.version{ii} = vernum{ii}; - end - elseif ischar(vernum) - vernum = str2cell(vernum,'.'); - if ~isnumber([vernum{:}]) - return; - end - obj.version = cell(length(vernum),1); - for ii=1:length(vernum) - obj.version{ii} = vernum{ii}; - end - end + obj.version = vernum; end @@ -113,73 +82,26 @@ function SetVersion(obj, vernum) end - % ---------------------------------------------------------------------------------- - function verstr = GetVersionStr(obj) - verstr = version2string(obj.version); - end - - % ---------------------------------------------------------------------------------- function filename = GetFilename(obj) filename = obj.outputFilename; end - % ---------------------------------------------------------------------------------- - function InitVersionStrFull(obj) - if isempty(obj.version) - return; - end - verstr = version2string(obj.version); - obj.versionStr = sprintf('GroupClass v%s', verstr); - end - - % ---------------------------------------------------------------------------------- function res = CompareVersions(obj, obj2) - res = 1; - if ~isproperty(obj, 'version') - return; - elseif ~ischar(obj2.version) && ~iscell(obj2.version) - return; - elseif ischar(obj2.version) - if ~isnumber(obj2.version) - return; - end - v2 = str2cell(obj2.version,'.'); - elseif iscell(obj2.version) - v2 = obj2.version; - end - v1 = obj.version; - - for ii=1:length(v1) - v1{ii} = str2num(v1{ii}); %#ok<*ST2NM> - end - for ii=1:length(v2) - v2{ii} = str2num(v2{ii}); + v1 = versionstr2num(obj.version); + v2 = versionstr2num(obj2.version); + if v1 == v2 + res = 0; + elseif v1 < v2 + res = 1; + elseif v1 > v2 + res = -1; end - % Now that we have the version numbers of both objects, we can - % do an actual numeric comparison + % TBD: not sure how to handle this. For now just return 0 res = 0; - for ii=1:max(length(v1), length(v2)) - if ii>length(v1) - res = -1; - break; - end - if ii>length(v2) - res = 1; - break; - end - if v1{ii}>v2{ii} - res = 1; - break; - end - if v1{ii}= 0 % Do a conditional copy of group from saved processing output file. Conditional copy copies ONLY % derived data, that is, only from procStream but NOT acqruired. We do not want to % overwrite the real acquired data loaded from acquisition files diff --git a/DataTree/ProcStream/ProcStreamClass.m b/DataTree/ProcStream/ProcStreamClass.m index 9ec5844a..801f6551 100644 --- a/DataTree/ProcStream/ProcStreamClass.m +++ b/DataTree/ProcStream/ProcStreamClass.m @@ -43,7 +43,6 @@ return; end obj.CreateDefault(); - obj.ExportProcStreamFunctions(false); end @@ -402,20 +401,24 @@ function SaveInitOutput(obj, pathname, filename) % ---------------------------------------------------------------------------------- function ExportProcStream(obj, filename, fcalls) - global logger + global logger + global cfg temp = obj.output.SetFilename(filename); if isempty(temp) return; end [p,f] = fileparts(temp); fname = [filesepStandard(p), f, '_processing.json']; - if obj.ExportProcStreamFunctions()==true + if strcmpi(cfg.GetValue('Export Processing Stream Functions'), 'yes') logger.Write('Saving processing stream %s:\n', fname); - appname = sprintf('%s, (v%s)', getNamespace(), getVernum(getNamespace())); + appname = sprintf('%s', getNamespace()); + vernum = sprintf('v%s', getVernum(appname)); dt = sprintf('%s', char(datetime(datetime, 'Format','MMMM d, yyyy, HH:mm:ss'))); mlActManCompressed = obj.CompressMlActMan(); tIncManCompressed = obj.CompresstIncMan(); - jsonstruct = struct('ApplicationName',appname, 'DateTime',dt, 'tIncMan',tIncManCompressed, 'mlActMan',mlActManCompressed, 'FunctionsCalls',{fcalls}); + jsonstruct = struct('ProcessingElement',f, 'ApplicationName',appname, 'Version',vernum, ... + 'Dependencies',obj.ExportProcStreamDependencies(), 'DateTime',dt, 'tIncMan',tIncManCompressed, ... + 'mlActMan',mlActManCompressed, 'FunctionCalls',{fcalls}); jsonStr = savejson('Processing', jsonstruct); fid = fopen(fname, 'w'); fwrite(fid, jsonStr, 'uint8'); @@ -429,10 +432,23 @@ function ExportProcStream(obj, filename, fcalls) end end end - end + end - % ---------------------------------------------------------------------------------- + + + % ---------------------------------------------------------------------------------- + function depStruct = ExportProcStreamDependencies(obj) + depStruct = struct(); + [d, v] = dependencies(); + for ii = 1:length(d) + eval( sprintf('depStruct.%s = ''v%s'';', d{ii}, v{ii}) ); + end + end + + + + % ---------------------------------------------------------------------------------- function nbytes = MemoryRequired(obj) nbytes(1) = obj.input.MemoryRequired(); nbytes(2) = obj.output.MemoryRequired(); @@ -1951,24 +1967,7 @@ function ExportMeanHRF_Alt(obj, filename, tblcells) end end - - - - methods (Static) - - % ---------------------------------------------------------------------------------- - function out = ExportProcStreamFunctions(arg) - persistent saveProcStream; - if nargin == 0 - out = saveProcStream; - return; - end - saveProcStream = arg; - out = saveProcStream; - end - end - end diff --git a/DataTree/TreeNodeClass.m b/DataTree/TreeNodeClass.m index 00cbb882..e1a4707e 100644 --- a/DataTree/TreeNodeClass.m +++ b/DataTree/TreeNodeClass.m @@ -1559,26 +1559,14 @@ function ClosePlots(obj, option) methods % ---------------------------------------------------------------------------------- - function ExportProcStreamFunctionsOpen(obj) - val = obj.cfg.GetValue('Export Processing Stream Functions'); - if strcmpi(val, 'yes') - obj.procStream.ExportProcStreamFunctions(true); - elseif strcmpi(val, 'no') - obj.procStream.ExportProcStreamFunctions(false); - end - end - - - - % ---------------------------------------------------------------------------------- - function ExportProcStreamFunctionsClose(obj) + function ExportProcStreamFunctions(obj) if ispathvalid([obj.path, obj.outputDirname, 'ProcStreamFunctionsSummary.txt']) try delete([obj.path, obj.outputDirname, 'ProcStreamFunctionsSummary.txt']) catch end end - if ~obj.procStream.ExportProcStreamFunctions() + if strcmpi(obj.cfg.GetValue('Export Processing Stream Functions'), 'no') return end obj.ExportProcStreamFunctionsSummary(); @@ -1617,7 +1605,13 @@ function ExportProcStreamFunctionsSummary(obj) fprintf(fid, '%s :\n', [fname, ext]); fprintf(fid, '%s\n', uint32('-') + uint32(zeros(1, length([fname, ext])+2))); txt = loadjson(procStreamFunctionsExportFilenames{ii}); - fcalls = txt.Processing.FunctionsCalls; + + % Backwards compatability + if isfield(txt.Processing,'FunctionCalls') + fcalls = txt.Processing.FunctionCalls; + elseif isfield(txt.Processing,'FunctionsCalls') + fcalls = txt.Processing.FunctionsCalls; + end for kk = 1:length(fcalls) fprintf(fid, '%s\n', fcalls{kk}); end diff --git a/DataTree/Version.txt b/DataTree/Version.txt index dc1de315..32bd932f 100644 --- a/DataTree/Version.txt +++ b/DataTree/Version.txt @@ -1 +1 @@ -1.6.0 +1.12.0 \ No newline at end of file diff --git a/DataTree/setpaths.m b/DataTree/setpaths.m index 2749149d..82d8f6b1 100644 --- a/DataTree/setpaths.m +++ b/DataTree/setpaths.m @@ -1,10 +1,19 @@ -function setpaths(options) +function setpaths(addremove) % % USAGE: % +% % 1. Add all search paths for this repo % setpaths +% +% % 2. Same as 1.. Add all search paths for this repo % setpaths(1) +% +% % 3. Add all search paths for this repo while removing search +% % paths of all similar workspaces +% setpaths(2) +% +% % 4. Remove all search paths for this repo % setpaths(0) % currdir = pwd; @@ -17,37 +26,34 @@ function setpaths(options) appname = 'DataTreeClass'; % Parse arguments - addremove = 1; - if ~exist('options','var') - options = ''; - elseif isnumeric(options) - if options == 0 - addremove = 0; - else - options = ''; - end + if ~exist('addremove','var') + addremove = 1; end % Add libraries on which DataTreeClass depends - submodules = addDependenciesSearchPaths(); + d = addDependenciesSearchPaths(); % Start logger only after adding library paths. Logger is in the Utils libary. - logger = InitLogger([], [pwd, '/setpaths']); + logger = Logger('setpaths'); % Create list of possible known similar apps that may conflic with current % app appNameExclList = {'DataTree','AtlasViewerGUI','Homer3','Homer2_UI','brainScape','ResolveCommonFunctions'}; appNameInclList = {}; - exclSearchList = {'.git','Data','Docs','*_install','*.app'}; + exclSearchList = {'.git','.idea','Data','Docs','*_install','*.app','submodules'}; appThis = filesepStandard_startup(pwd); appThisPaths = findDotMFolders(appThis, exclSearchList); + if addremove == 0 if ~isempty(which('deleteNamespace.m')) deleteNamespace(appname); end removeSearchPaths(appThis); return; + elseif addremove == 2 + appNameExclList = [appNameExclList, appNameInclList]; + appNameInclList = {}; end appExclList = {}; @@ -116,12 +122,12 @@ function setpaths(options) end if ~isempty(which('setpaths_proprietary.m')) - setpaths_proprietary(options); + setpaths_proprietary(addremove); end warning('on','MATLAB:rmpath:DirNotFound'); - PrintSystemInfo(logger, ['DataTreeClass'; submodules(:,end)]); + PrintSystemInfo(logger, ['DataTreeClass'; d]); logger.CurrTime('Setpaths completed on '); logger.Close(); cd(currdir); @@ -139,10 +145,20 @@ function setpaths(options) +% --------------------------------------------------- +function d = dependencies() +d = {}; +submodules = parseGitSubmodulesFile(pwd); +temp = submodules(:,1); +for ii = 1:length(temp) + [~, d{ii,1}] = fileparts(temp{ii}); +end + % --------------------------------------------------- function setpermissions(appPath) if isunix() || ismac() + global logger if ~isempty(strfind(appPath, '/bin')) %#ok<*STREMP> cmd = sprintf('chmod 755 %s/*\n', appPath); logger.Write(cmd); @@ -164,7 +180,7 @@ function addSearchPaths(appPaths) else delimiter = ':'; end - appPaths = str2cell_startup(p, delimiter); + appPaths = str2cell(p, delimiter); end for kk = 1:length(appPaths) if strfind(appPaths{kk}, '.git') @@ -208,24 +224,29 @@ function removeSearchPaths(app) % ---------------------------------------------------- -function submodules = addDependenciesSearchPaths() -global logger -submodules = downloadDependencies(); -for ii = 1:size(submodules) - submodulespath = [pwd, '/', submodules{ii,end}]; - submodulespath(submodulespath=='\') = '/'; - if ~exist(submodulespath,'dir') +function d = addDependenciesSearchPaths() +if exist([pwd, '/Utils/submodules'],'dir') + addpath([pwd, '/Utils/submodules'],'-end'); +end +d = dependencies(); +for ii = 1:length(d) + rootpath = findFolder(pwd, d{ii}); + rootpath(rootpath=='\') = '/'; + if ispathvalid_startup([rootpath, '/Shared'],'dir') + rootpath = [rootpath, '/Shared']; + end + if ~exist(rootpath,'dir') printMethod(sprintf('ERROR: Could not find required dependency %s\n', d{ii})); continue; end - printMethod(sprintf('Adding searchpaths for submodule %s\n', submodulespath)); - addSearchPaths(submodulespath); + addSearchPaths(rootpath); end + % ----------------------------------------------------------------------------- -function [C,k] = str2cell_startup(str, delimiters, options) +function [C,k] = str2cell(str, delimiters, options) % Option tells weather to keep leading whitespaces. % (Trailing whitespaces are always removed) @@ -287,6 +308,30 @@ function printMethod(msg) end + + +% ------------------------------------------------------------------------- +function dirpath = findFolder(repo, dirname) +dirpath = ''; +if ~exist('repo','var') + repo = filesepStandard_startup(pwd); +end +dirpaths = findDotMFolders(repo, {'.git', '.idea'}); + +for ii = 1:length(dirpaths) + [~, f, e] = fileparts(dirpaths{ii}(1:end-1)); + if strcmp(dirname, [f,e]) + dirpath = dirpaths{ii}; + break; + end + if ispathvalid_startup([dirname, dirpaths{ii}]) + dirpath = dirpaths{ii}; + break; + end +end + + + % ------------------------------------------------------------------------- function dotmfolders = findDotMFolders(subdir, exclList) global logger diff --git a/FuncRegistry/UserFunctions/hmrR_GLM.m b/FuncRegistry/UserFunctions/hmrR_GLM.m index c41b3bad..0549faa3 100644 --- a/FuncRegistry/UserFunctions/hmrR_GLM.m +++ b/FuncRegistry/UserFunctions/hmrR_GLM.m @@ -165,6 +165,7 @@ tInc = tIncAuto{iBlk}; dt = t(2) - t(1); + fq = 1/dt; nPre = round(trange(1)/dt); nPost = round(trange(2)/dt); nTpts = size(y,1); @@ -600,7 +601,7 @@ ytmp = y(lstInc,conc,lstML); for chanIdx=1:length(lstML) ytmp2 = y(lstInc,conc,lstML(chanIdx)); - [dmoco, beta, tstat(:,lstML(chanIdx),conc), pval(:,lstML(chanIdx),conc), sigma, CovB(:,:,lstML(chanIdx),conc), dfe, w, P, f] = ar_glm_final(squeeze(ytmp2),At(lstInc,:)); + [dmoco, beta, tstat(:,lstML(chanIdx),conc), pval(:,lstML(chanIdx),conc), sigma, CovB(:,:,lstML(chanIdx),conc), dfe, w, P, f] = ar_glm_final(squeeze(ytmp2),At(lstInc,:), round(fq*2)); foo(:,lstML(chanIdx),conc)=beta; ytmp(:,1,chanIdx) = dmoco; %We also need to keep my version of "Yvar" and "Bvar" diff --git a/FuncRegistry/UserFunctions/iWLS/ar_glm_final.m b/FuncRegistry/UserFunctions/iWLS/ar_glm_final.m index 1f5d1636..2dccf7a3 100644 --- a/FuncRegistry/UserFunctions/iWLS/ar_glm_final.m +++ b/FuncRegistry/UserFunctions/iWLS/ar_glm_final.m @@ -1,4 +1,4 @@ -function [dmoco, beta, tstat, pval, sigma, CovB, dfe, w, P, f] = ar_glm_final( d,X,tune ) +function [dmoco, beta, tstat, pval, sigma, CovB, dfe, w, P, f] = ar_glm_final( d,X,Pmax ) % preallocation dmoco = zeros(size(d)); @@ -22,7 +22,6 @@ res = y-X*B; - Pmax = 10; a = robust_ar_fit(res, Pmax); f = [1; -a(2:end)]; diff --git a/FuncRegistry/UserFunctions/mlAct_Initialize.m b/FuncRegistry/UserFunctions/mlAct_Initialize.m index 3c4af46d..6722b275 100644 --- a/FuncRegistry/UserFunctions/mlAct_Initialize.m +++ b/FuncRegistry/UserFunctions/mlAct_Initialize.m @@ -6,13 +6,13 @@ % end k = 1:size(ml,1); if isvector(mlAct0) && (length(mlAct0) == length(k)) - mlAct = [ ml(:,1:2), mlAct0, ml(:,4) ]; -elseif isvector(mlAct0) && (length(mlAct0) > length(k)) - if size(ml,2) == 2 - mlAct = [ ml(k,1:2), ones(size(ml(k,:),1),2) ]; + if size(ml,2)==4 + mlAct = [ ml(:,1:2), mlAct0, ml(:,4) ]; else - mlAct = [ ml(k,1:2), ones(size(ml(k,:),1),1), ml(k,4) ]; + mlAct = [ ml(:,1:2), mlAct0, ones(size(ml,1),1) ]; end +elseif isvector(mlAct0) && (length(mlAct0) > length(k)) + mlAct = [ ml(k,1:2), ones(size(ml(k,:),1),1), ml(k,4) ]; elseif size(mlAct0,1) == length(k) mlAct = mlAct0; elseif size(ml,2) == 2 diff --git a/FuncRegistry/Version.txt b/FuncRegistry/Version.txt index bf6d67a5..589268e6 100644 --- a/FuncRegistry/Version.txt +++ b/FuncRegistry/Version.txt @@ -1 +1 @@ -1.2.0 +1.3.0 \ No newline at end of file diff --git a/MainGUI/MainGUI.fig b/MainGUI/MainGUI.fig index 53b631311d768fc4c35160b7d9bae1611aa4fff1..decd896119a715f279074c12977da440e4c7eee2 100644 GIT binary patch literal 101114 zcma%iQ*+-$cI{n22PBROrntZA|Hi6l@G#%@S85(YbW2Nvf1gWB{1( zYnqKy&P&A;{sJSEmgjp^umI0bnhi#Ank-hZB)ginFz?=8W0NEXyV~H87dShkoH~`v z`0;HMG?If zKPYimkayKDm+6i5wNb2YH;)IMdIU^)pI?6jPu+J>)i3rfsd$m~L^)P%wi?ONap^+E z9UXMaZR4uiB=N(FsAU#ANaKSZtL-Qi38uF9Iz^rO>DjYK#Coh`!6zaoteVECQiIcX z89~g(fG|oIqqb^k;e%>Ot>mxjZx(|h=QJ`672N51a6vSEdkZ_ zdqakJ(AM-r!Uz(HXjhPro`}s~kEya7t8KL2&j%ot#I$xX>aU;tJuy~q?G>faz+8Ya z5to0d>F4=l(Fg9G5r9rV08b&9YV$XtzZky% zBItIjm4+|F@knU`r+0Q@2Xwu8nZ$IBZkM>W~z?EpaYng-vm61?D{_1CFvMjM4qvdO`Bd z*o=ZX9_WrQjGyTqk@So0 zKL4*j_Uq~>AjZe&a6w&$-|^k&^6?;lL4M=LaKZXB(GPwcaMX8ic~g4{i^m&Rs7W@lvHGr`wf zKWnJ4RJvW#WSw>1v|b!;@?)JWif zCI~4Cf{T+zaqxzZzfh3iMTb+FJS+3QPF%iGmIKJ2-Tf_`HK_YljiIimm!aFu?p@qP zvNo-b2qcIlJIDWS8V|3|)Rw(zz)QgKrWK7jq5Mv?#uS!aIAA-@?)j}7xL`|{k*1>3 zC-qGf4VD_ff3=W{ivW;1d0KewUpsbEm3@40`i-g>1eU}4aCM3OP&&^a&+FV%`S>ap zp4{}5#p9m$&`%mGAQg5`+P+bcAEW&ilPG)VMR%Qvb={_HC$ikc)~ciQCOP{{pO)e5 znrkokAS~w0_Crb_|7&|D;IK;q#E&pvYX~6x=&8MPJ8-m2an{bh>~X2eJ8Jq%`Ewv^ z!)(pGnFwY{RfbJ~|H7B>5#!V^E_dZa-iM(0)fRxcY+vjgw@2e8AEluqFzGuBURU!< zI0ocP+)!nTOOh5kNnCcF`|D@?_ZPxXNqA<$YlKF5hQ~hHKPH`0D}#*oqA|E%0i;m; zjPV1UlP|tbXO=EIOuk-qcitPP0;9jxsge1vAH8&+#@HyI+u%yJU&&=7yjg9vLaG8p ztU}fgH=8XNxWzUat!TGntTOj|3*?8;J6J2X`RWUkVz2{T$3tB3gEVSL4aojx2}mnCq;L*|xQPw6iXB z*Y2)~49Nag9#cLso1oR({G|WSYJ7cNYQox}iDGT*Nlz614*A3TLpxCH_n$)gCZkdR zmEZKUCB^hVB=$D9>AROYw~Xx(>Anw9eHWEbj5H`jj`{f`@m`_404Y*|fMixmo66UD z<)m2z4eVvLeR|vY#=@}n=kVv)F(BO^;Q#rRnVy-A9q1s*Yi-c(f?8gjpouTqHX$Q5 z;Y23iD8oB(OE#sD^&;1m!+zRjh2L-r5xY#D5Iwzn<(a~yo*DY&hj3LYH zgOXi+vY-ijk`hb`$M{EBNs~3E;odM~3i|Aö+P|@1c3xa%IiECNJ(MJxG0!pU} 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ð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

as~906_LIqWlY{8<7vP3VoYmC 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 literal 100572 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 N-B1h+=4Z$0{|Co*w+{dS diff --git a/MainGUI/MainGUI.m b/MainGUI/MainGUI.m index ba4fe351..75876724 100644 --- a/MainGUI/MainGUI.m +++ b/MainGUI/MainGUI.m @@ -860,29 +860,46 @@ function menuItemPlotProbeGUI_Callback(hObject, ~, handles) LaunchChildGuiFromMenu('PlotProbeGUI', hObject, GetDatatype(handles), maingui.condition); % -------------------------------------------------------------------- -% function menuItemPlotProbe2_Callback(hObject, ~, handles) -% global maingui - -%%%% It will be in the next release -% procElem = maingui.dataTree.currElem; -% % Derived data that we want to save in a Snirf file. -% % To save in a snirf file we need to create a SnirfClass -% % object. A SnirfClass object is DataTree's implementation -% % of the Snirf format. -% data(1) = procElem.procStream.output.dcAvg; +function menuItemPlotProbe2_Callback(hObject, ~, handles) +global maingui +procElem = maingui.dataTree.currElem; +% Derived data that we want to save in a Snirf file. +% To save in a snirf file we need to create a SnirfClass +% object. A SnirfClass object is DataTree's implementation +% of the Snirf format. +data(1) = procElem.procStream.output.dcAvg; % data(2) = procElem.procStream.output.dod; -% -% % To complete SnirfClass object arguments we need to supply these -% % which we get from acquired data of the first run associated with -% % our procElem. -% probe = procElem.acquired.probe; -% stim = procElem.acquired.stim; -% metaDataTags = procElem.acquired.metaDataTags; -% -% obj = SnirfClass(data, stim, probe, metaDataTags); -% -% % call PlotProbe2 GUI -% PlotProbe2(obj); + +% To complete SnirfClass object arguments we need to supply these +% which we get from acquired data of the first run associated with +% our procElem. +if strcmp(procElem.type,'sess') + maingui.dataTree.SetCurrElem(procElem.iGroup, procElem.iSubj, procElem.iSess, 1); + maingui.dataTree.LoadCurrElem() + procElem = maingui.dataTree.currElem; + maingui.dataTree.SetCurrElem(procElem.iGroup, procElem.iSubj, procElem.iSess); + maingui.dataTree.LoadCurrElem() +elseif strcmp(procElem.type,'subj') + maingui.dataTree.SetCurrElem(procElem.iGroup, procElem.iSubj, 1, 1); + maingui.dataTree.LoadCurrElem() + procElem = maingui.dataTree.currElem; + maingui.dataTree.SetCurrElem(procElem.iGroup, procElem.iSubj); + maingui.dataTree.LoadCurrElem() +elseif strcmp(procElem.type,'group') + maingui.dataTree.SetCurrElem(procElem.iGroup, 1, 1, 1); + maingui.dataTree.LoadCurrElem() + procElem = maingui.dataTree.currElem; + maingui.dataTree.SetCurrElem(procElem.iGroup); + maingui.dataTree.LoadCurrElem() +end +probe = procElem.acquired.probe; +stim = procElem.acquired.stim; +metaDataTags = procElem.acquired.metaDataTags; + +obj = SnirfClass(data, stim, probe, metaDataTags); + +% call PlotProbe2 GUI +PlotProbe2(obj); diff --git a/PlotProbe2GUI/PlotProbe2.fig b/PlotProbe2GUI/PlotProbe2.fig new file mode 100644 index 0000000000000000000000000000000000000000..d161488ecf649f6da01318f25e18458c2e49cb9e GIT binary patch literal 52297 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 literal 0 HcmV?d00001 diff --git a/PlotProbe2GUI/PlotProbe2.m b/PlotProbe2GUI/PlotProbe2.m new file mode 100644 index 00000000..9135b113 --- /dev/null +++ b/PlotProbe2GUI/PlotProbe2.m @@ -0,0 +1,826 @@ +function varargout = PlotProbe2(varargin) +% PLOTPROBE2 MATLAB code for PlotProbe2.fig +% PLOTPROBE2, by itself, creates a new PLOTPROBE2 or raises the existing +% singleton*. +% +% H = PLOTPROBE2 returns the handle to a new PLOTPROBE2 or the handle to +% the existing singleton*. +% +% PLOTPROBE2('CALLBACK',hObject,eventData,handles,...) calls the local +% function named CALLBACK in PLOTPROBE2.M with the given input arguments. +% +% PLOTPROBE2('Property','Value',...) creates a new PLOTPROBE2 or raises the +% existing singleton*. Starting from the left, property value pairs are +% applied to the GUI before PlotProbe2_OpeningFcn gets called. An +% unrecognized property name or invalid value makes property application +% stop. All inputs are passed to PlotProbe2_OpeningFcn via varargin. +% +% *See GUI Options on GUIDE's Tools menu. Choose "GUI allows only one +% instance to run (singleton)". +% +% See also: GUIDE, GUIDATA, GUIHANDLES + +% Edit the above text to modify the response to help PlotProbe2 + +% Last Modified by GUIDE v2.5 22-Mar-2022 16:55:43 + +% Return if snirf object was not passed +if isempty(varargin) + disp('Please pass snirf object as an argument'); + return +end + +% Begin initialization code - DO NOT EDIT +gui_Singleton = 1; +gui_State = struct('gui_Name', mfilename, ... + 'gui_Singleton', gui_Singleton, ... + 'gui_OpeningFcn', @PlotProbe2_OpeningFcn, ... + 'gui_OutputFcn', @PlotProbe2_OutputFcn, ... + 'gui_LayoutFcn', [] , ... + 'gui_Callback', []); +if nargin && ischar(varargin{1}) + gui_State.gui_Callback = str2func(varargin{1}); +end + +if nargout + [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:}); +else + gui_mainfcn(gui_State, varargin{:}); +end +% End initialization code - DO NOT EDIT + + +% --- Executes just before PlotProbe2 is made visible. +function PlotProbe2_OpeningFcn(hObject, eventdata, handles, varargin) +% This function has no output args, see OutputFcn. +% hObject handle to figure +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +% varargin command line arguments to PlotProbe2 (see VARARGIN) + +% Choose default command line output for PlotProbe2 +handles.output = hObject; + +% Update handles structure +guidata(hObject, handles); + +% UIWAIT makes PlotProbe2 wait for user response (see UIRESUME) +% uiwait(handles.figure1); +if ischar(varargin{1}) + [fPath, fName, fExt] = fileparts(varargin{1}); + probePath = erase(fPath,'\derivatives\homer'); + snirffiles = dir([probePath '\*.snirf']); + if ~isempty(snirffiles) + snirfObj = SnirfClass([snirffiles(1).folder filesep snirffiles(1).name]); + load(varargin{1}); + datasnirf = SnirfClass(output.dcAvg, [], [], []); + snirfObj.data = datasnirf.data; + else + return + end +else + snirfObj = varargin{1}; +end +dataTypeLabels = {}; +dataTypeOrder = {}; +measList = []; +wavelengths = GetWls(snirfObj.probe); +HbXList = {'None'}; +dODList = {'None'}; +for v = 1:length(snirfObj.data) + for u = 1:length(snirfObj.data(v).measurementList) + dataTypeLabel = GetDataTypeLabel(snirfObj.data(v).measurementList(u)); + + % Make sure while saving snirf this is a string not cell array, + % althogh its an issue but it should save consistantly but saves string + % sometimes and as cell as sometimes + if iscell(dataTypeLabel) + dataTypeLabel = strcat((char(dataTypeLabel))'); +% snirfObj.data(v).measurementList(u) = dataTypeLabel; + end + + if strcmp(dataTypeLabel,'dOD') + wavelengthIndex = GetWavelengthIndex(snirfObj.data(v).measurementList(u)); + wavelength = wavelengths(wavelengthIndex); + dataTypeLabel = [dataTypeLabel '_' num2str(wavelength)]; + end + + if ~any(strcmp(dataTypeLabels,dataTypeLabel)) + dataTypeLabels{end+1} = dataTypeLabel; + end + srcIdx = GetSourceIndex(snirfObj.data(v).measurementList(u)); + detIdx = GetDetectorIndex(snirfObj.data(v).measurementList(u)); + if isempty(measList) + measList = [srcIdx, detIdx]; + elseif sum(ismember(measList, [srcIdx, detIdx], 'rows')) == 0 + measList = [measList; [srcIdx, detIdx]]; + end + + if u == 1 + minAmp = min(snirfObj.data(v).dataTimeSeries(:,u)); + maxAmp = max(snirfObj.data(v).dataTimeSeries(:,u)); + snirfObj.data(v).dataTimeSeries(:,u) = snirfObj.data(v).dataTimeSeries(:,u) -(minAmp+ maxAmp)/2; + minAmp = minAmp-(minAmp+ maxAmp)/2; + maxAmp = maxAmp-(minAmp+ maxAmp)/2; + if strcmp(dataTypeLabel,'HRF HbO') || strcmp(dataTypeLabel,'HRF HbO') || strcmp(dataTypeLabel,'HRF HbO') + dataTypeOrder{end+1} = 'HRF HbX'; + elseif contains(dataTypeLabel,'dOD') + dataTypeOrder{end+1} = 'dOD'; + end + else + minAmp = min(minAmp,min(snirfObj.data(v).dataTimeSeries(:,u))); + maxAmp = max(maxAmp,max(snirfObj.data(v).dataTimeSeries(:,u))); + snirfObj.data(v).dataTimeSeries(:,u) = snirfObj.data(v).dataTimeSeries(:,u) -(minAmp+ maxAmp)/2; + minAmp = minAmp-(minAmp+ maxAmp)/2; + maxAmp = maxAmp-(minAmp+ maxAmp)/2; + end + end + handles.data.minAmp(v) = minAmp; + handles.data.maxAmp(v) = maxAmp; +end + +for u = 1:length(dataTypeLabels) + if contains(dataTypeLabels{u},'HRF') | contains(dataTypeLabels{u},'HbO') | contains(dataTypeLabels{u},'HbR') | contains(dataTypeLabels{u},'HbT') + if ~any(strcmp(HbXList,dataTypeLabels{u})) + HbXList{end+1} = dataTypeLabels{u}; + end + set(handles.radiobutton_HbX, 'Enable', 'on'); + set(handles.listbox_selectActivity, 'Enable', 'on'); + end + + if contains(dataTypeLabels{u},'dOD') + if ~any(strcmp(dODList,dataTypeLabels{u})) + dODList{end+1} = dataTypeLabels{u}; + end + set(handles.radiobutton_dOD, 'Enable', 'on'); + set(handles.listbox_selectActivity, 'Enable', 'on'); + end +end + +sPos = snirfObj.probe.sourcePos3D; +dPos = snirfObj.probe.detectorPos3D; +min_dist = 0; +max_dist = 0; +for v = 1:length(snirfObj.data) + for u = 1:length(snirfObj.data(v).measurementList) + srcIdx = GetSourceIndex(snirfObj.data(v).measurementList(u)); + detIdx = GetDetectorIndex(snirfObj.data(v).measurementList(u)); + channel_dist = sqrt(sum((sPos(srcIdx,:) - dPos(detIdx ,:)).^2)); + min_dist = min(min_dist, channel_dist); + max_dist = max(max_dist, channel_dist); + end +end +set(handles.edit_minDistForDisplay, 'String', min_dist); +set(handles.edit_maxDistForDisplay, 'String', max_dist); + +condition_names = {' All'}; +for u = 1:length(snirfObj.stim) + condition_names{u+1} = GetName(snirfObj.stim(u)); +end +set(handles.listbox_selectConditions,'String',condition_names); + +handles.data.snirfObj = snirfObj; +handles.data.measList = measList; +handles.data.HbXList = HbXList; +handles.data.dODList = dODList; +handles.data.dataTypeOrder = dataTypeOrder; + +if isempty(snirfObj.probe.landmarkPos2D) + set(handles.radiobutton_refPointsAsLabels,'Enable','Off'); + set(handles.radiobutton_refPointsAsCircles,'Enable','Off'); +end + +if ~isempty(HbXList) + set(handles.radiobutton_HbX, 'Value', 1.0); + set(handles.listbox_selectActivity,'String',HbXList); +end + +if ~isempty(dODList) && isempty(HbXList) + set(handles.radiobutton_dOD, 'Value', 1.0); + set(handles.listbox_selectActivity,'String',dODList); +end + +% % process data for displaying +% if ~isempty(snirfObj.data.dataTimeSeries) +% nT = snirfObj.data.dataTimeSeries(:,1); +% else +% nT = 0; +% end + +% handles.data.display_activity = zeros(nT,length(dataTypeLabels), +display(handles); +guidata(hObject,handles) + + +function display(handles) + +if isfield(handles,'data') & isfield(handles.data, 'snirfObj') + + snirfObj = handles.data.snirfObj; + measList = handles.data.measList; + sPos = snirfObj.probe.sourcePos2D; + dPos = snirfObj.probe.detectorPos2D; + + sourcePos3D = snirfObj.probe.sourcePos3D; + detectorPos3D = snirfObj.probe.detectorPos3D; + Distances=((sPos(measList(:,1),1) - dPos(measList(:,2),1)).^2 +... + (sPos(measList(:,1),2) - dPos(measList(:,2),2)).^2 +... + (sPos(measList(:,1),3) - dPos(measList(:,2),3)).^2).^0.5; + + sdMin = min([sPos;dPos]) - mean(Distances(:)); + sdMax = max([sPos;dPos]) + mean(Distances(:)); + + sdWid = sdMax(1) - sdMin(1); + sdHgt = sdMax(2) - sdMin(2); + + sd2axScl = max(sdWid,sdHgt); + + if isempty(snirfObj.probe.landmarkPos2D) + sPos = sPos / sd2axScl; + dPos = dPos / sd2axScl; + end + + nAcross=length(unique([sPos(:,1); dPos(:,1)]))+1; + nUp=length(unique([sPos(:,2); dPos(:,2)]))+1; + +% nAcross=length(unique([sourcePos3D(:,1); sourcePos3D(:,1)]))+1; +% nUp=length(unique([sourcePos3D(:,2); sourcePos3D(:,2)]))+1; + +% axFactor = [1,1]; + plot_Xscale = str2double(get(handles.edit_Xscale,'String')); + plot_Yscale = str2double(get(handles.edit_Yscale,'String')); + axWid = plot_Xscale * 1/nAcross; + axHgt = plot_Yscale * 1/nUp; + + axXoff=mean([sPos(:,1);dPos(:,1)])-.5; + axYoff=mean([sPos(:,2);dPos(:,2)])-.5; + + axes(handles.axes1); + set(handles.axes1, 'xlim', [0,1], 'ylim', [0,1]); + + % Clear axes + cla(handles.axes1); + axis off; + % Plot the optodes on the axes + if ismac() || islinux() + fs = 14; + else + fs = 9; + end + hold on + + for idx2=1:size(sPos,1) + xa = sPos(idx2,1) - axXoff; + ya = sPos(idx2,2) - axYoff; + if get(handles.checkbox_optodesAsCircles,'Value') + ht=plot(xa,ya,'.','markersize',10); + else + ht=text(xa,ya,sprintf('S%d',idx2)); + set(ht,'fontweight','bold','fontsize',fs) + end + set(ht,'color',[1 0 0]) + + end + for idx2=1:size(dPos,1) + xa = dPos(idx2,1) - axXoff; + ya = dPos(idx2,2) - axYoff; + if get(handles.checkbox_optodesAsCircles,'Value') + ht=plot(xa,ya,'.','markersize',10); + else + ht=text(xa,ya,sprintf('D%d',idx2)); + set(ht,'fontweight','bold','fontsize',fs) + end + set(ht,'color',[0 0 1]) + end + + if ~isempty(snirfObj.probe.landmarkPos2D) + if get(handles.radiobutton_refPointsAsLabels,'Value') || get(handles.radiobutton_refPointsAsCircles,'Value') + refPos = snirfObj.probe.landmarkPos2D; + if isempty(snirfObj.probe.landmarkPos2D) + refPos = refPos / sd2axScl; + end + for idx2 =1:size(refPos,1) + xa = refPos(idx2,1) - axXoff; + ya = refPos(idx2,2) - axYoff; + if get(handles.radiobutton_refPointsAsCircles,'Value') + ht=plot(xa,ya,'.','markersize',10); + elseif get(handles.radiobutton_refPointsAsLabels,'Value') + ht=text(xa,ya,snirfObj.probe.landmarkLabels{idx2}); + set(ht,'fontweight','bold','fontsize',6) + end + set(ht,'color',[0 0 0]) + end + end + end + + color=[1.00 0.00 0.00; + 0.00 0.00 1.00; + 0.00 1.00 0.00; + 1.00 0.00 1.00; + 0.00 1.00 1.00; + 0.50 0.80 0.30 + ]; + + t = snirfObj.data(1).time; + minT = min(t); + maxT = max(t); + EXPLODE_THRESH = 0.02; + EXPLODE_VECTOR = [0.0, 0.0]; + xyas = []; + minT = min(t); + maxT = max(t); + + channel_min_dist = str2double(get(handles.edit_minDistForDisplay, 'String')); + channel_max_dist = str2double(get(handles.edit_maxDistForDisplay, 'String')); +% contents = cellstr(get(handles.listbox_selectConditions,'String')); + selected_conditions_index = get(handles.listbox_selectConditions,'Value'); + data_index = 0; + selected_display_activities = {}; + + if get(handles.radiobutton_HbX,'Value') + selected_display_activities_index = get(handles.listbox_selectActivity,'Value'); + all_activities = get(handles.listbox_selectActivity,'String'); + selected_display_activities = all_activities(selected_display_activities_index); + if ~contains(selected_display_activities,'None') + data_index = find(contains(handles.data.dataTypeOrder,'HRF HbX')); + end + elseif get(handles.radiobutton_dOD,'Value') + selected_display_activities_index = get(handles.listbox_selectActivity,'Value'); + all_activities = get(handles.listbox_selectActivity,'String'); + selected_display_activities = all_activities(selected_display_activities_index); + if ~contains(selected_display_activities,'None') + data_index = find(contains(handles.data.dataTypeOrder,'dOD')); + end + end + if data_index~= 0 + SigCh = [1; 36; 48; 50]; + for u = 1:length(snirfObj.data(data_index).measurementList) + activityConditionIndex = GetCondition(snirfObj.data(data_index).measurementList(u)); + if any(selected_conditions_index == 1) || any(selected_conditions_index == activityConditionIndex+1) + srcIdx = GetSourceIndex(snirfObj.data(data_index).measurementList(u)); + detIdx = GetDetectorIndex(snirfObj.data(data_index).measurementList(u)); + + channel_dist = sqrt(sum((sourcePos3D(srcIdx,:) - detectorPos3D(detIdx ,:)).^2)); + if channel_dist >= channel_min_dist & channel_dist <= channel_max_dist + xa = (sPos(srcIdx,1) + dPos(detIdx ,1))/2 - axXoff; + ya = (sPos(srcIdx,2) + dPos(detIdx ,2))/2 - axYoff; + + dataTypeLabel = GetDataTypeLabel(snirfObj.data(data_index).measurementList(u)); + if iscell(dataTypeLabel) + dataTypeLabel = strcat((char(dataTypeLabel))'); + end + + % plot a line between source and detector + if get(handles.checkbox_displayMeasurementLine,'Value') +% if get(handles.checkbox_displayHbO, 'Value') || ... +% get(handles.checkbox_displayHbR, 'Value') || ... +% get(handles.checkbox_displayHbT, 'Value') + xPos = [sPos(srcIdx,1) dPos(detIdx,1)] - axXoff; + yPos = [sPos(srcIdx,2) dPos(detIdx,2)] - axYoff; +% plot(xPos ,yPos,'--','Color',[0.5 0.5 0.5]) + if contains(dataTypeLabel,'HbO') + if ismember(u,SigCh)==0 + plot(xPos ,yPos,'--','Color',[0.5 0.5 0.5]) + elseif ismember(u,SigCh)==1 + plot(xPos ,yPos,'-','Color','red') + end + end +% end + end + for i = 1:size(xyas, 1) + if sqrt((xyas(i, 1) - xa)^2 + (xyas(i, 2) - ya)^2) < EXPLODE_THRESH + xa = xa + EXPLODE_VECTOR(1); + ya = ya + EXPLODE_VECTOR(2); + end + end + xT = xa-axWid/4 + axWid*((t-minT)/(maxT-minT))/2; + xyas = [xyas; [xa, ya]]; + Avg = snirfObj.data(data_index).dataTimeSeries(:,u); + % minAmp=squeeze(min(min(Avg))); + % maxAmp=squeeze(max(max(Avg))); + cmin = handles.data.minAmp(data_index); + cmax = handles.data.maxAmp(data_index); + % Avg = Avg-(cmin+cmax)/2; + AvgT = ya-axHgt/4 + axHgt*((Avg-cmin)/(cmax-cmin))/2; + % cmin = min(AvgT); + % cmax = max(AvgT); + % AvgT = AvgT-(cmin+cmax)/2; + + + if any(contains(selected_display_activities,dataTypeLabel)) & contains(dataTypeLabel,'HbO') + plot( xT, AvgT,'color',color(1,:)); + elseif any(contains(selected_display_activities,dataTypeLabel)) & contains(dataTypeLabel,'HbR') + plot( xT, AvgT,'color',color(2,:)); + elseif any(contains(selected_display_activities,dataTypeLabel)) & contains(dataTypeLabel,'HbT') + plot( xT, AvgT,'color',color(3,:)); + elseif any(contains(selected_display_activities,dataTypeLabel)) + plot( xT, AvgT,'color',color(4,:)); + end + end + end + end + end + hold off + if get(handles.checkbox_axisImage,'Value') + axis image + end +end + + + + +% --- Outputs from this function are returned to the command line. +function varargout = PlotProbe2_OutputFcn(hObject, eventdata, handles) +% varargout cell array for returning output args (see VARARGOUT); +% hObject handle to figure +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Get default command line output from handles structure +varargout{1} = handles.output; + + +% --- Executes on button press in checkbox_displayHbO. +function checkbox_displayHbO_Callback(hObject, eventdata, handles) +% hObject handle to checkbox_displayHbO (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of checkbox_displayHbO +set(handles.checkbox_displayOD1,'Value',0) +set(handles.checkbox_displayOD2,'Value',0) +display(handles) + + +% --- Executes on button press in checkbox_displayHbR. +function checkbox_displayHbR_Callback(hObject, eventdata, handles) +% hObject handle to checkbox_displayHbR (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of checkbox_displayHbR +set(handles.checkbox_displayOD1,'Value',0) +set(handles.checkbox_displayOD2,'Value',0) +display(handles) + + +% --- Executes on button press in checkbox_displayHbT. +function checkbox_displayHbT_Callback(hObject, eventdata, handles) +% hObject handle to checkbox_displayHbT (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of checkbox_displayHbT +set(handles.checkbox_displayOD1,'Value',0) +set(handles.checkbox_displayOD2,'Value',0) +display(handles) + + +% --- Executes on button press in checkbox_displayOD1. +function checkbox_displayOD1_Callback(hObject, eventdata, handles) +% hObject handle to checkbox_displayOD1 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of checkbox_displayOD1 +set(handles.checkbox_displayHbO,'Value',0) +set(handles.checkbox_displayHbR,'Value',0) +set(handles.checkbox_displayHbT,'Value',0) +display(handles) + + +% --- Executes on button press in checkbox_displayOD2. +function checkbox_displayOD2_Callback(hObject, eventdata, handles) +% hObject handle to checkbox_displayOD2 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of checkbox_displayOD2 +set(handles.checkbox_displayHbO,'Value',0) +set(handles.checkbox_displayHbR,'Value',0) +set(handles.checkbox_displayHbT,'Value',0) +display(handles) + + +% --- Executes on button press in checkbox_displayOD3. +function checkbox_displayOD3_Callback(hObject, eventdata, handles) +% hObject handle to checkbox_displayOD3 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of checkbox_displayOD3 + + +% --- Executes on button press in checkbox_displayOD4. +function checkbox_displayOD4_Callback(hObject, eventdata, handles) +% hObject handle to checkbox_displayOD4 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of checkbox_displayOD4 + + + +function edit_Xscale_Callback(hObject, eventdata, handles) +% hObject handle to edit_Xscale (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of edit_Xscale as text +% str2double(get(hObject,'String')) returns contents of edit_Xscale as a double + +display(handles) + + +% --- Executes during object creation, after setting all properties. +function edit_Xscale_CreateFcn(hObject, eventdata, handles) +% hObject handle to edit_Xscale (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function edit_Yscale_Callback(hObject, eventdata, handles) +% hObject handle to edit_Yscale (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of edit_Yscale as text +% str2double(get(hObject,'String')) returns contents of edit_Yscale as a double + +display(handles) + + +% --- Executes during object creation, after setting all properties. +function edit_Yscale_CreateFcn(hObject, eventdata, handles) +% hObject handle to edit_Yscale (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on button press in pushbutton_XscaleDown. +function pushbutton_XscaleDown_Callback(hObject, eventdata, handles) +% hObject handle to pushbutton_XscaleDown (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +Xscale = str2double(get(handles.edit_Xscale,'String')); +set(handles.edit_Xscale,'String',num2str(Xscale*0.5)); +display(handles) + + +% --- Executes on button press in pushbutton_YscaleDown. +function pushbutton_YscaleDown_Callback(hObject, eventdata, handles) +% hObject handle to pushbutton_YscaleDown (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +Yscale = str2double(get(handles.edit_Yscale,'String')); +set(handles.edit_Yscale,'String',num2str(Yscale*0.5)); +display(handles) + + +% --- Executes on button press in pushbutton_XscaleUP. +function pushbutton_XscaleUP_Callback(hObject, eventdata, handles) +% hObject handle to pushbutton_XscaleUP (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +Xscale = str2double(get(handles.edit_Xscale,'String')); +set(handles.edit_Xscale,'String',num2str(Xscale*1.5)); +display(handles) + + +% --- Executes on button press in pushbutton_YscaleUP. +function pushbutton_YscaleUP_Callback(hObject, eventdata, handles) +% hObject handle to pushbutton_YscaleUP (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +Yscale = str2double(get(handles.edit_Yscale,'String')); +set(handles.edit_Yscale,'String',num2str(Yscale*1.5)); +display(handles) + + + +function edit_minDistForDisplay_Callback(hObject, eventdata, handles) +% hObject handle to edit_minDistForDisplay (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of edit_minDistForDisplay as text +% str2double(get(hObject,'String')) returns contents of edit_minDistForDisplay as a double + +display(handles) + + +% --- Executes during object creation, after setting all properties. +function edit_minDistForDisplay_CreateFcn(hObject, eventdata, handles) +% hObject handle to edit_minDistForDisplay (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function edit_maxDistForDisplay_Callback(hObject, eventdata, handles) +% hObject handle to edit_maxDistForDisplay (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of edit_maxDistForDisplay as text +% str2double(get(hObject,'String')) returns contents of edit_maxDistForDisplay as a double + +display(handles) + + +% --- Executes during object creation, after setting all properties. +function edit_maxDistForDisplay_CreateFcn(hObject, eventdata, handles) +% hObject handle to edit_maxDistForDisplay (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on button press in checkbox_optodesAsCircles. +function checkbox_optodesAsCircles_Callback(hObject, eventdata, handles) +% hObject handle to checkbox_optodesAsCircles (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of checkbox_optodesAsCircles + +display(handles) + + +% --- Executes on button press in radiobutton_refPointsAsLabels. +function radiobutton_refPointsAsLabels_Callback(hObject, eventdata, handles) +% hObject handle to radiobutton_refPointsAsLabels (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of radiobutton_refPointsAsLabels + +if get(handles.radiobutton_refPointsAsLabels,'Value') + set(handles.radiobutton_refPointsAsCircles,'Value',0); +end + +display(handles) + + +% --- Executes on button press in radiobutton_refPointsAsCircles. +function radiobutton_refPointsAsCircles_Callback(hObject, eventdata, handles) +% hObject handle to radiobutton_refPointsAsCircles (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +if get(handles.radiobutton_refPointsAsCircles,'Value') + set(handles.radiobutton_refPointsAsLabels,'Value',0); +end + +display(handles) + +% Hint: get(hObject,'Value') returns toggle state of radiobutton_refPointsAsCircles + + +% --- Executes on button press in checkbox_displayMeasurementLine. +function checkbox_displayMeasurementLine_Callback(hObject, eventdata, handles) +% hObject handle to checkbox_displayMeasurementLine (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of checkbox_displayMeasurementLine + +display(handles) + +% --- Executes on selection change in popupmenu_selectConditions. +function popupmenu_selectConditions_Callback(hObject, eventdata, handles) +% hObject handle to popupmenu_selectConditions (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns popupmenu_selectConditions contents as cell array +% contents{get(hObject,'Value')} returns selected item from popupmenu_selectConditions + + +% --- Executes during object creation, after setting all properties. +function popupmenu_selectConditions_CreateFcn(hObject, eventdata, handles) +% hObject handle to popupmenu_selectConditions (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: popupmenu controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on selection change in listbox_selectConditions. +function listbox_selectConditions_Callback(hObject, eventdata, handles) +% hObject handle to listbox_selectConditions (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns listbox_selectConditions contents as cell array +% contents{get(hObject,'Value')} returns selected item from listbox_selectConditions +display(handles) + +% --- Executes during object creation, after setting all properties. +function listbox_selectConditions_CreateFcn(hObject, eventdata, handles) +% hObject handle to listbox_selectConditions (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: listbox controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on button press in checkbox_axisImage. +function checkbox_axisImage_Callback(hObject, eventdata, handles) +% hObject handle to checkbox_axisImage (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of checkbox_axisImage + +display(handles) + + +% --- Executes on button press in radiobutton_HbX. +function radiobutton_HbX_Callback(hObject, eventdata, handles) +% hObject handle to radiobutton_HbX (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of radiobutton_HbX + +if strcmp(get(handles.radiobutton_dOD,'Enable'),'on') + set(handles.radiobutton_dOD,'Value',0); + set(handles.listbox_selectActivity,'Value',1); + set(handles.listbox_selectActivity,'String',handles.data.HbXList); +end + + +% --- Executes on button press in radiobutton_dOD. +function radiobutton_dOD_Callback(hObject, eventdata, handles) +% hObject handle to radiobutton_dOD (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of radiobutton_dOD + +if strcmp(get(handles.radiobutton_HbX,'Enable'),'on') + set(handles.radiobutton_HbX,'Value',0); + set(handles.listbox_selectActivity,'Value',1); + set(handles.listbox_selectActivity,'String',handles.data.dODList); +end + + +% --- Executes on selection change in listbox_selectActivity. +function listbox_selectActivity_Callback(hObject, eventdata, handles) +% hObject handle to listbox_selectActivity (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns listbox_selectActivity contents as cell array +% contents{get(hObject,'Value')} returns selected item from listbox_selectActivity + +display(handles) + +% --- Executes during object creation, after setting all properties. +function listbox_selectActivity_CreateFcn(hObject, eventdata, handles) +% hObject handle to listbox_selectActivity (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: listbox controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end diff --git a/PlotProbe2GUI/README.md b/PlotProbe2GUI/README.md new file mode 100644 index 00000000..48b52058 --- /dev/null +++ b/PlotProbe2GUI/README.md @@ -0,0 +1,14 @@ +# PlotProbe2 + +### Instructions to run PlotProbe2 + +1. Open Matlab +2. Go to PlotProbe2 folder on Matlab +3. Add PlotProbe2 to matlab path by running ``` addpath(genpath('.')) ``` command on matlab command window +4. Go to the folder where snirf file is located or use probe.snirf file present in the PlotProbe2 folder +5. Run below commands on matlab command window +``` +obj = SnirfClass('probe.snirf'); +PlotProbe2(obj); +``` +6. Note that your filename could be different from ```probe.snirf```, so pass appropriate filename to ```SnirfClass``` diff --git a/Utils/Shared/Logger.m b/Utils/Shared/Logger.m index a051a99e..71b420ae 100644 --- a/Utils/Shared/Logger.m +++ b/Utils/Shared/Logger.m @@ -12,6 +12,7 @@ methods + % ------------------------------------------------- function self = Logger(appname, options) @@ -79,6 +80,7 @@ self.chapter = struct('maxsize',1e6, 'offset',0, 'number',1); end + % ------------------------------------------------- function val = Filter(self, options) @@ -95,11 +97,13 @@ end end - - + % ------------------------------------------------- function Write(self, s, varargin) %#ok<*INUSL> + if iscell(s) + s = [s{:}]; %#ok + end vars = ''; for ii = 1:length(varargin) varargin{ii} = replaceSpecialChars(varargin{ii}); @@ -129,6 +133,7 @@ function Write(self, s, varargin) %#ok<*INUSL> end + % ------------------------------------------------- function WriteNoNewline(self, s, options, hwait) if ~exist('options','var') @@ -141,6 +146,7 @@ function WriteNoNewline(self, s, options, hwait) end + % ------------------------------------------------- function WriteStr(self, s, options, hwait) if ~exist('options','var') @@ -171,6 +177,7 @@ function WriteStr(self, s, options, hwait) end + % ------------------------------------------------- function WriteNoFmt(self, s, options, hwait) if ~exist('options','var') @@ -234,6 +241,7 @@ function WriteNoFmt(self, s, options, hwait) end + % ------------------------------------------------- function Error(self, msg, options, hwait) if self.fhandle < 0 @@ -271,6 +279,7 @@ function Error(self, msg, options, hwait) end + % ------------------------------------------------- function Open(self) try @@ -283,7 +292,8 @@ function Open(self) end end - + + % ------------------------------------------------- function Close(self, appname) if ~exist('appname','var') || isempty(appname) @@ -312,6 +322,7 @@ function Close(self, appname) end + % ------------------------------------------------- function Delete(self, appname) if ~exist('appname','var') || isempty(appname) @@ -340,6 +351,7 @@ function Delete(self, appname) end + % ------------------------------------------------- function SetDebugLevel(self, options) if ~exist('options','var') || isempty(options) @@ -349,36 +361,42 @@ function SetDebugLevel(self, options) end + % ------------------------------------------------- function val = FileOnly(self) val = self.options.FILE_ONLY; end + % ------------------------------------------------- function val = Null(self) val = self.options.NULL; end + % ------------------------------------------------- function val = ConsoleOnly(self) val = self.options.CONSOLE_ONLY; end + % ------------------------------------------------- function val = ConsoleAndFile(self) val = self.options.CONSOLE_AND_FILE; end + % ------------------------------------------------- function val = Debug(self) val = self.options.DEBUG; end + % ------------------------------------------------- function val = ProgressBar(self) val = self.options.PROGRESS_BAR; @@ -390,6 +408,7 @@ function SetDebugLevel(self, options) [~, filename] = fileparts(obj.filename); end + % --------------------------------------------------------------- function InitChapters(self) @@ -401,6 +420,7 @@ function InitChapters(self) end + % --------------------------------------------------------------- function ResetChapter(self) if self.fhandle < 0 @@ -412,6 +432,7 @@ function ResetChapter(self) end + % --------------------------------------------------------------- function CheckFileSize(self) if self.fhandle < 0 @@ -427,6 +448,7 @@ function CheckFileSize(self) end + % --------------------------------------------------------------- function b = IsOpen(self) b = false; diff --git a/Utils/Shared/MenuBox.m b/Utils/Shared/MenuBox.m index ebc29b91..44a9a6db 100644 --- a/Utils/Shared/MenuBox.m +++ b/Utils/Shared/MenuBox.m @@ -1,4 +1,4 @@ -function [selection, hf] = MenuBox(msg, bttns, relativePos, textLineWidth, options) +function [answer, hf] = MenuBox(msg, bttns, relativePos, textLineWidth, options) % % SYNTAX: @@ -18,12 +18,19 @@ % q = MenuBox('Please select option',{'option1','option2','option3'},'centerright',[],'dontAskAgainOptions'); % q = MenuBox('Please select option',{'option1','option2','option3'},[],75,'dontAskAgain'); % +% % Next few examples use radio button selection style +% q = MenuBox('Please select option',{'option1','option2','option3'},[],[],'radiobutton'); +% q = MenuBox('Please select option',{'option1','option2','option3'},[],[],'dontAskAgain:radiobutton'); +% q = MenuBox('Please select option',{'option1','option2','option3'},'upperright',80,'askEveryTime:radiobutton'); +% +hf = []; global bttnIds -bttnIds = 0; +global selection +global selectionStyle -DEBUG=0; -DEBUG2=0; +bttnIds = 0; +selection = 0; % Parse args if iscell(msg) @@ -46,123 +53,145 @@ title = 'MENU'; -bttnstrlenmax = 0; -for ii = 1:length(bttns) - if length(bttns{ii})>bttnstrlenmax - bttnstrlenmax = length(bttns{ii}); - end +if optionExists(options, 'radiobutton') + selectionStyle = 'radiobutton'; +else + selectionStyle = 'pushbutton'; end -bttnstrlenmin = 7; % Syntax for special call of MenuBox to ONLY get back the selection of % "ask/don't ask" checkbox strings, then exit function checkboxes = getCheckboxes(options); if isempty(msg) - selection = checkboxes; + answer = checkboxes; return; end -length(find(msg == sprintf('\n'))); %#ok<*SPRINTFN> +fs(1) = 10; +fs(2) = 8; +if ismac() + fs = fs + 4; +end -nchar = length(msg); -nNewLines = length(find(msg == sprintf('\n'))); %#ok<*SPRINTFN> -ncheckboxes = length(checkboxes); +ncheckboxes = length(checkboxes); nbttns = length(bttns)+ncheckboxes; -if bttnstrlenmax +nLines = ceil(length(msg) / Wtext)*1.5; +Htext = max([nNewLines, nLines]); +HtextGap0 = 1; +HtextGap = 2; + +% Initial X sizes and positions of buttons +Wbttn = 65; +WbttnMin = 20; +XbttnOffset = 5; + + +% Calculate standard width of buttons +WbttnMaxActual = length(bttns{1}); +for ii = 1:length(bttns) + if length(bttns{ii}) > WbttnMaxActual + WbttnMaxActual = length(bttns{ii}); + end +end +if WbttnMaxActual < Wbttn + Wbttn = WbttnMaxActual; +end +if WbttnMaxActual < WbttnMin + Wbttn = WbttnMin; end -Hbttn = 2.7; -if Wbttn < textLineWidth - Wtext = textLineWidth; % In char units -else - Wtext = 1.1 * Wbttn; +% Initial Y size and position of buttons +Hbttn = 1; +HbttnGap = 2; + +% Calculate standard height of buttons +for ii = 1:length(bttns) + temp = ceil(length(bttns{ii}) / Wbttn); + if temp > Hbttn + Hbttn = temp; + end end -Htext = round(nchar / Wtext)+4 + nNewLines; -% Position/dimensions in the X direction -a = 5; -Wfig = Wtext+0.1*Wtext; % GUI width -% Position/dimensions in the Y direction -vertgap = 1.2; -Hfig = (Htext+vertgap+1) + nbttns*(Hbttn+vertgap) + vertgap*1.5; +% Character size doesn't quite equal character units so we compensate by multiplying by +% scaling factor in the x and y directions +Wbttn = Wbttn*1.2; +Hbttn = Hbttn*1.2; -% Get position of parent GUI in character units -hParent = get(groot,'CurrentFigure'); -if isempty(hParent) - hParent = 0; -end -set(hParent, 'units','characters'); -if hParent==0 - posParent = get(hParent,'MonitorPositions'); + +% Figure size and position +if strcmpi(selectionStyle, 'radiobutton') + Hc = 7; else - posParent = get(hParent, 'position'); + Hc = 4; end +XtextOffset = Wtext/8; +Wfig = Wtext + 2*XtextOffset; +HfigBottom = nbttns * (Hbttn + HbttnGap) + Hc; +HfigTop = HtextGap0 + Htext + HtextGap; +Hfig = HfigTop + HfigBottom; -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Calculate GUI objects position/dimensions in the Y and Y -% directions, in characters units -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -hf = figure('numbertitle', 'off', 'menubar','none', 'toolbar','none', 'name',title); -set(hf, 'visible','off'); +hf = figure('numbertitle', 'off', 'menubar','none', 'toolbar','none', 'name',title, 'resize','on'); +%set(hf, 'visible','off'); set(hf, 'units','characters'); -% Determine optimal position of MenuBox relative to parent GUI -[pX, pY] = positionRelative(hf, posParent, relativePos); -posBox = [pX, pY, Wfig, Hfig]; - -if DEBUG - fprintf('posBox = [%0.1f, %0.1f, %0.1f, %0.1f]\n', posBox(1), posBox(2), posBox(3), posBox(4)); +hParent = get(groot,'CurrentFigure'); +if isempty(hParent) + hParent = hf; end +set(hParent, 'units','characters'); +posParent = get(hParent, 'position'); - -% Set GUI position/size -set(hf, 'position', posBox); -p = get(hf, 'position'); - -% Display message -if DEBUG2 - fprintf('message position: [%0.1f, %0.1f, %0.1f, %0.1f]\n', a, p(4)-Htext-vertgap, Wtext, Htext); - ht = uicontrol('parent',hf, 'style','text', 'units','characters', 'string',msg, ... - 'position',[a, p(4)-(Htext+vertgap+1), Wtext, Htext], 'horizontalalignment','left', ... - 'backgroundcolor',[.2,.2,.2], 'foregroundcolor',[.9,.9,.9]); +% Determine optimal position of MenuBox relative to parent GUI +if hf == hParent + pX = posParent(1); + pY = posParent(2); else - ht = uicontrol('parent',hf, 'style','text', 'units','characters', 'string',msg, ... - 'position',[a, p(4)-(Htext+vertgap+1), Wtext, Htext], 'horizontalalignment','left'); + [pX, pY] = positionRelative(hf, posParent, relativePos); +end +posBox = [pX, pY, Wfig, Hfig]; +set(hf, 'position', posBox); +if strcmpi(selectionStyle, 'radiobutton') + DispSaveCancelBttns(hf); end +pF = get(hf, 'position'); -% Draw button options -hb = zeros(nbttns,1); -p = zeros(nbttns,4); -TextPos = get(ht, 'position'); +YbttnStart = Htext + HtextGap; +ht = uicontrol('parent',hf, 'style','text', 'units','characters', 'string',msg, 'fontsize',fs(1), ... + 'position',[XtextOffset, pF(4)-(HtextGap0+Htext), Wtext, Htext], 'horizontalalignment','left', ... + 'userdata',2); for k = 1:nbttns - Ypfk = TextPos(2) - k*(Hbttn+vertgap); - p(k,:) = [2*a, Ypfk, Wbttn, Hbttn]; - - if DEBUG2 - fprintf('%d) %s: p1 = [%0.1f, %0.1f, %0.1f, %0.1f]\n', k, bttns{k}, p(k,1), p(k,2), p(k,3), p(k,4)); - end - + Ypfk = pF(4) - (YbttnStart + k*(Hbttn+HbttnGap)); + p = [XbttnOffset, Ypfk, Wbttn, Hbttn]; if k > (nbttns-ncheckboxes) val = GetCheckboxValue(k, nbttns, ncheckboxes, options); hb(k) = uicontrol('parent',hf, 'style','checkbox', 'string',checkboxes{k-(nbttns-ncheckboxes)}, 'units','characters', ... - 'position',[p(k,1), p(k,2), 2*length(checkboxes{k-(nbttns-ncheckboxes)}), p(k,4)], 'value',val, ... + 'position',[p(1), p(2), 2*length(checkboxes{k-(nbttns-ncheckboxes)}), p(4)], 'value',val, ... 'tag',sprintf('%d', k-(nbttns-ncheckboxes)), 'callback',{@checkboxDontAskOptions_Callback, hf}); checkboxDontAskOptions_Callback(hb(k), [], hf); else - hb(k) = uicontrol('parent',hf, 'style','pushbutton', 'string',bttns{k}, 'units','characters', 'position',p(k,:), ... - 'tag',sprintf('%d', k), 'callback',@pushbuttonGroup_Callback); + if strcmpi(selectionStyle, 'radiobutton') + hb = uicontrol('parent',hf, 'style',selectionStyle, 'string','', 'units','characters', 'position',[p(1), p(2), 4, p(4)], ... + 'tag',sprintf('%d', k), 'callback',@pushbuttonGroup_Callback, 'backgroundcolor',[0.80, 0.80, 0.80]); + + uicontrol('parent',hf, 'style','text', 'string',bttns{k}, 'units','characters', 'position',[p(1)+4, p(2), p(3), p(4)], ... + 'horizontalalignment','left', 'fontsize',fs(2), 'userdata',2, 'backgroundcolor',[1.0, 1.0, 1.0]); + else + uicontrol('parent',hf, 'style',selectionStyle, 'string',bttns{k}, 'units','characters', 'position',[p(1), p(2), p(3), p(4)+Hbttn/2], ... + 'tag',sprintf('%d', k), 'fontsize',fs(2), 'callback',@pushbuttonGroup_Callback, 'userdata',2, 'backgroundcolor',[1.0, 1.0, 1.0]); + end end end -% Need to make sure position data is saved in pixel units at end of function -% to as these are the units used to reposition GUI later if needed setGuiFonts(hf); p = guiOutsideScreenBorders(hf); @@ -172,12 +201,11 @@ % Change units back to characters set(hf, 'units','characters'); - -% Wait for user to respond before exiting +normalizeObjPos(hf); % Wait for user to respond before exiting t = 0; -while bttnIds(1)==0 && ishandles(hf) +while selection(1)==0 && ishandles(hf) t=t+1; pause(.2); if mod(t,30)==0 @@ -188,27 +216,46 @@ end end - % Call this callback in case default is preselected in the code, that way % we make sure that if one of the checkboxes is preselected not by user % action but by code, that we detect that. checkboxDontAskOptions_Callback([], [], hf) -% Assign button selctions to function output -selection = bttnIds; +answer = selection; if ishandles(hf) delete(hf); else - selection=0; + if strcmpi(selectionStyle, 'pushbutton') + answer = 0; + end end + + % ------------------------------------------------------------- function pushbuttonGroup_Callback(hObject, ~, ~) global bttnIds +global selection +global selectionStyle bttnIds(1) = str2num(get(hObject, 'tag')); +if strcmpi(selectionStyle, 'pushbutton') + selection = bttnIds; + return; +end +hp = get(hObject,'parent'); +hc = get(hp, 'children'); +for ii = 1:length(hc) + if strcmpi(get(hc(ii),'type'), 'uicontrol') + if strcmpi(get(hc(ii),'style'), 'radiobutton') + if ~strcmpi(get(hc(ii), 'tag'), get(hObject, 'tag')) + set(hc(ii), 'value',0); + end + end + end +end @@ -230,6 +277,7 @@ function checkboxDontAskOptions_Callback(hObject, ~, hf) end else for ii = 1:length(hb) + if strcmp(get(hb(ii), 'type'),'uicontrol') if strcmp(get(hb(ii), 'style'),'checkbox') if get(hb(ii), 'value') checkboxId = str2num(get(hb(ii), 'tag')); @@ -238,6 +286,7 @@ function checkboxDontAskOptions_Callback(hObject, ~, hf) end end end +end if isempty(checkboxId) return; @@ -347,3 +396,47 @@ function checkboxDontAskOptions_Callback(hObject, ~, hf) end end + + +% ------------------------------------------------------------- +function [hBttnSave, hBttnExit] = DispSaveCancelBttns(hf) +uhf0 = get(hf, 'units'); +set(hf, 'units','characters'); +phf = get(hf, 'position'); +set(hf, 'position',[phf(1), phf(2), phf(3), phf(4)+phf(4)*.2]); +xsize = 20; +ysize = 3; +xpos = .1*phf(3); +ypos = .1*phf(4); +hBttnSave = uicontrol(hf, 'Style','pushbutton', 'FontSize',15, 'Units','normalized', 'String','SAVE', 'units','characters', 'Position',[xpos, ypos, xsize, ysize]); +hBttnExit = uicontrol(hf, 'Style','pushbutton', 'FontSize',15, 'Units','normalized', 'String','CANCEL', 'units','characters', 'Position',[phf(3) - (xsize + .1*phf(3)), ypos, xsize, ysize]); +hBttnSave.Callback = @cfgSave; +hBttnExit.Callback = @cfgExit; +set(hf, 'units',uhf0) + + + +% ------------------------------------------------------------- +function cfgSave(hObject, ~) %#ok<*DEFNU> +global bttnIds +global selection +selection = bttnIds; +close; + + + +% ------------------------------------------------------------- +function cfgExit(~,~) +close; + + + +% ------------------------------------------------------------- +function normalizeObjPos(hf) +hc = get(hf, 'children'); +for ii = 1:length(hc) + set(hc(ii), 'units','normalized'); +end + + + diff --git a/Utils/Shared/Version.txt b/Utils/Shared/Version.txt index 977221bd..7b5753f5 100644 --- a/Utils/Shared/Version.txt +++ b/Utils/Shared/Version.txt @@ -1 +1 @@ -1.1.2 +1.4.6 \ No newline at end of file diff --git a/Utils/Shared/configSettingsGUI.m b/Utils/Shared/configSettingsGUI.m index ec5dbaed..812060fb 100644 --- a/Utils/Shared/configSettingsGUI.m +++ b/Utils/Shared/configSettingsGUI.m @@ -113,7 +113,10 @@ function InitializeGuiStruct(filenames, options) cfgGui.ysizeGap = 1*fy; cfgGui.xsizeGap = 2*fx; -cfgGui.fontsizeVals = 9; +cfgGui.fontsizes = [15, 11, 10, 9]; +if ismac() + cfgGui.fontsizes = cfgGui.fontsizes+4; +end cfgGui.posParam = [.10,.10,.80,.80]; cfgGui.options = options; @@ -194,14 +197,14 @@ function ResizeGui(hf, nParamsPerCol, ~) ip = cfgGui.nParamsPerCol*(i-1) + j; if ip>np if np==0 - uicontrol(hf, 'Style','text', 'string','CONFIG FILE IS EMPTY', 'FontSize',11, ... + uicontrol(hf, 'Style','text', 'string','CONFIG FILE IS EMPTY', 'FontSize',cfgGui.fontsizes(2), ... 'fontweight','bold', 'units',cfgGui.units, 'Position',posPanel, 'foregroundcolor',[.6,.3,.1]); end break; end % Draw param panel - hp(ip) = uipanel('parent',hf, 'Title',cfgGui.filedata.GetParamName(ip), 'FontSize',10, 'fontweight','bold', 'foregroundcolor',[.6,.3,.1], ... + hp(ip) = uipanel('parent',hf, 'Title',cfgGui.filedata.GetParamName(ip), 'FontSize',cfgGui.fontsizes(3), 'fontweight','bold', 'foregroundcolor',[.6,.3,.1], ... 'units',cfgGui.units, 'Position',posPanel); % Draw param values control within panel. Note all controls have same relative position within panel @@ -210,12 +213,12 @@ function ResizeGui(hf, nParamsPerCol, ~) pval = ''; end if isempty(cfgGui.filedata.GetParamValueOptions(ip)) - hv = uicontrol(hp(ip), 'Style','edit', 'string',pval, 'FontSize',cfgGui.fontsizeVals, 'fontweight','bold', 'Tag',cfgGui.filedata.GetParamName(ip), ... - 'units','normalized', 'position',cfgGui.posParam); + hv = uicontrol(hp(ip), 'Style','edit', 'string',pval, 'FontSize',cfgGui.fontsizes(4), 'fontweight','bold', 'Tag',cfgGui.filedata.GetParamName(ip), ... + 'units','normalized', 'position',cfgGui.posParam); else hv = uicontrol(hp(ip), 'Style','popupmenu', 'string',cfgGui.filedata.GetParamValueOptions(ip), ... - 'FontSize',cfgGui.fontsizeVals, 'fontweight','bold', 'Tag',cfgGui.filedata.GetParamName(ip), ... - 'units','normalized', 'position',cfgGui.posParam); + 'FontSize',cfgGui.fontsizes(4), 'fontweight','bold', 'Tag',cfgGui.filedata.GetParamName(ip), ... + 'units','normalized', 'position',cfgGui.posParam); k = find(strcmp(cfgGui.filedata.GetParamValueOptions(ip), pval)); if isempty(k) set(hv, 'string',[{''}; cfgGui.filedata.GetParamValueOptions(ip)]); @@ -247,10 +250,10 @@ function ResizeGui(hf, nParamsPerCol, ~) k = 5; end xoffset = cfgGui.xsizeTotal/k; -hBttnSave = uicontrol(hf, 'Style','pushbutton', 'FontSize',15, 'Units',cfgGui.units, 'String','SAVE', ... +hBttnSave = uicontrol(hf, 'Style','pushbutton', 'FontSize',cfgGui.fontsizes(1), 'Units',cfgGui.units, 'String','SAVE', ... 'Position', [xoffset, cfgGui.ysizeTotal-(cfgGui.ysizeParamsAll+cfgGui.ysizeParam), cfgGui.xsizeBttn, cfgGui.ysizeBttn]); hBttnSave.Callback = @cfgSave; -hBttnExit = uicontrol(hf, 'Style','pushbutton', 'FontSize',15, 'Units',cfgGui.units, 'String','EXIT', ... +hBttnExit = uicontrol(hf, 'Style','pushbutton', 'FontSize',cfgGui.fontsizes(1), 'Units',cfgGui.units, 'String','EXIT', ... 'Position', [cfgGui.xsizeTotal-(cfgGui.xsizeBttn+xoffset), cfgGui.ysizeTotal-(cfgGui.ysizeParamsAll+cfgGui.ysizeParam), cfgGui.xsizeBttn, cfgGui.ysizeBttn]); hBttnExit.Callback = @cfgExit; setappdata(hBttnSave, 'backgroundcolororiginal',hBttnSave.BackgroundColor); @@ -290,7 +293,15 @@ function setVal(hObject, ~, hBttnSave) % ------------------------------------------------------------- function cfgSave(hObject, ~) %#ok<*DEFNU> global cfgGui +global cfg cfgGui.filedata.Save(); + +% This is the only place in configSettingsGUI that we access global cfg. +% We only use it to update the global variable when we to save changes +if isa(cfg, 'ConfigFileClass') + cfg.Update(); +end + hObject.BackgroundColor = getappdata(hObject, 'backgroundcolororiginal'); hObject.ForegroundColor = getappdata(hObject, 'foregroundcolororiginal'); if optionExists(cfgGui.options, {'keepopen','stayopen'}) diff --git a/Utils/Shared/convert_optodepos_to_circlular_2D_pos.m b/Utils/Shared/convert_optodepos_to_circlular_2D_pos.m new file mode 100644 index 00000000..e5a848a6 --- /dev/null +++ b/Utils/Shared/convert_optodepos_to_circlular_2D_pos.m @@ -0,0 +1,14 @@ +function xy = convert_optodepos_to_circlular_2D_pos(pos, T, norm_factor) +pos = [pos ones(size(pos,1),1)]; +pos_unit_sphere = pos*T; +pos_unit_sphere_norm = sqrt(sum(pos_unit_sphere.^2,2)); +pos_unit_sphere = pos_unit_sphere./pos_unit_sphere_norm ; + +[azimuth,elevation, ~] = cart2sph(pos_unit_sphere(:,1),pos_unit_sphere(:,2),pos_unit_sphere(:,3)); +elevation = pi/2-elevation; +[x,y] = pol2cart(azimuth,elevation); % get plane coordinates +xy = [x y]; +xy = xy/norm_factor; % set maximum to unit length +end + + diff --git a/Utils/Shared/dependencies.m b/Utils/Shared/dependencies.m new file mode 100644 index 00000000..33c5d900 --- /dev/null +++ b/Utils/Shared/dependencies.m @@ -0,0 +1,12 @@ +function [apps, vers, appdirs] = dependencies() +apps = {}; +vers = {}; +appdirs = {}; +submodules = parseGitSubmodulesFile(getAppDir()); +temp = submodules(:,1); +for ii = 1:length(temp) + [~, apps{ii,1}] = fileparts(temp{ii}); + vers{ii,1} = getVernum(apps{ii,1}); + appdirs{ii,1} = submodules{ii,2}; +end + diff --git a/Utils/Shared/getCurrTime.m b/Utils/Shared/getCurrTime.m new file mode 100644 index 00000000..378141fe --- /dev/null +++ b/Utils/Shared/getCurrTime.m @@ -0,0 +1,4 @@ +function [dateNum, dateStr] = getCurrTime() +ds = char(datetime('now','TimeZone','local','Format','yyyy-MM-dd HH:mm:ss')); +[dateNum, dateStr] = datestr2datenum(ds); + diff --git a/Utils/Shared/getVernum.m b/Utils/Shared/getVernum.m index e6773940..13327873 100644 --- a/Utils/Shared/getVernum.m +++ b/Utils/Shared/getVernum.m @@ -1,12 +1,25 @@ -function v = getVernum(appname, appdir) +function [v, appname] = getVernum(appname, appdir) v = ''; if ~exist('appname','var') || isempty(appname) - [~,f,e] = fileparts(pwd); + appname = ''; + if ~exist('appdir','var') || isempty(appdir) + appdir = getAppDir(); + end + if isempty(appdir) + return + end + if appdir(end)=='/' || appdir(end)=='\' + appdir(end) = ''; + end + [~,f,e] = fileparts(appdir); appname = [f,e]; end if ~exist('appdir','var') || isempty(appdir) appdir = getAppDir(); end +if isempty(appdir) + return +end libdir = '/Shared'; if isdeployed() p = appdir; diff --git a/Utils/Shared/guiOutsideScreenBorders.m b/Utils/Shared/guiOutsideScreenBorders.m index 7964a9c0..20d74c35 100644 --- a/Utils/Shared/guiOutsideScreenBorders.m +++ b/Utils/Shared/guiOutsideScreenBorders.m @@ -22,6 +22,13 @@ Ps = get(0,'MonitorPositions'); p = get(hObject,'position'); +% Don't do anything if within screen borders in all directions +if ((p(1)+p(3)) < 1) && (p(1) > 0) + if ((p(2)+p(4)) < 1) && (p(2) > 0) + return + end +end + % To work correctly for mutiple sceens, Ps must be sorted in ascending order Ps = sort(Ps,'ascend'); diff --git a/Utils/jsonlab/AUTHORS.txt b/Utils/Shared/jsonlab/AUTHORS.txt similarity index 100% rename from Utils/jsonlab/AUTHORS.txt rename to Utils/Shared/jsonlab/AUTHORS.txt diff --git a/Utils/jsonlab/ChangeLog.txt b/Utils/Shared/jsonlab/ChangeLog.txt similarity index 100% rename from Utils/jsonlab/ChangeLog.txt rename to Utils/Shared/jsonlab/ChangeLog.txt diff --git a/Utils/jsonlab/Contents.m b/Utils/Shared/jsonlab/Contents.m similarity index 100% rename from Utils/jsonlab/Contents.m rename to Utils/Shared/jsonlab/Contents.m diff --git a/Utils/jsonlab/LICENSE_BSD.txt b/Utils/Shared/jsonlab/LICENSE_BSD.txt similarity index 100% rename from Utils/jsonlab/LICENSE_BSD.txt rename to Utils/Shared/jsonlab/LICENSE_BSD.txt diff --git a/Utils/jsonlab/LICENSE_GPLv3.txt b/Utils/Shared/jsonlab/LICENSE_GPLv3.txt similarity index 100% rename from Utils/jsonlab/LICENSE_GPLv3.txt rename to Utils/Shared/jsonlab/LICENSE_GPLv3.txt diff --git a/Utils/jsonlab/README.txt b/Utils/Shared/jsonlab/README.txt similarity index 100% rename from Utils/jsonlab/README.txt rename to Utils/Shared/jsonlab/README.txt diff --git a/Utils/jsonlab/base64decode.m b/Utils/Shared/jsonlab/base64decode.m similarity index 100% rename from Utils/jsonlab/base64decode.m rename to Utils/Shared/jsonlab/base64decode.m diff --git a/Utils/jsonlab/base64encode.m b/Utils/Shared/jsonlab/base64encode.m similarity index 100% rename from Utils/jsonlab/base64encode.m rename to Utils/Shared/jsonlab/base64encode.m diff --git a/Utils/jsonlab/decodevarname.m b/Utils/Shared/jsonlab/decodevarname.m similarity index 100% rename from Utils/jsonlab/decodevarname.m rename to Utils/Shared/jsonlab/decodevarname.m diff --git a/Utils/jsonlab/encodevarname.m b/Utils/Shared/jsonlab/encodevarname.m similarity index 100% rename from Utils/jsonlab/encodevarname.m rename to Utils/Shared/jsonlab/encodevarname.m diff --git a/Utils/jsonlab/fast_match_bracket.m b/Utils/Shared/jsonlab/fast_match_bracket.m similarity index 100% rename from Utils/jsonlab/fast_match_bracket.m rename to Utils/Shared/jsonlab/fast_match_bracket.m diff --git a/Utils/jsonlab/filterjsonmmap.m b/Utils/Shared/jsonlab/filterjsonmmap.m similarity index 100% rename from Utils/jsonlab/filterjsonmmap.m rename to Utils/Shared/jsonlab/filterjsonmmap.m diff --git a/Utils/jsonlab/getfromjsonpath.m b/Utils/Shared/jsonlab/getfromjsonpath.m similarity index 100% rename from Utils/jsonlab/getfromjsonpath.m rename to Utils/Shared/jsonlab/getfromjsonpath.m diff --git a/Utils/jsonlab/gzipdecode.m b/Utils/Shared/jsonlab/gzipdecode.m similarity index 100% rename from Utils/jsonlab/gzipdecode.m rename to Utils/Shared/jsonlab/gzipdecode.m diff --git a/Utils/jsonlab/gzipencode.m b/Utils/Shared/jsonlab/gzipencode.m similarity index 100% rename from Utils/jsonlab/gzipencode.m rename to Utils/Shared/jsonlab/gzipencode.m diff --git a/Utils/jsonlab/isoctavemesh.m b/Utils/Shared/jsonlab/isoctavemesh.m similarity index 100% rename from Utils/jsonlab/isoctavemesh.m rename to Utils/Shared/jsonlab/isoctavemesh.m diff --git a/Utils/jsonlab/jdatadecode.m b/Utils/Shared/jsonlab/jdatadecode.m similarity index 100% rename from Utils/jsonlab/jdatadecode.m rename to Utils/Shared/jsonlab/jdatadecode.m diff --git a/Utils/jsonlab/jdataencode.m b/Utils/Shared/jsonlab/jdataencode.m similarity index 100% rename from Utils/jsonlab/jdataencode.m rename to Utils/Shared/jsonlab/jdataencode.m diff --git a/Utils/jsonlab/jload.m b/Utils/Shared/jsonlab/jload.m similarity index 100% rename from Utils/jsonlab/jload.m rename to Utils/Shared/jsonlab/jload.m diff --git a/Utils/jsonlab/jsave.m b/Utils/Shared/jsonlab/jsave.m similarity index 100% rename from Utils/jsonlab/jsave.m rename to Utils/Shared/jsonlab/jsave.m diff --git a/Utils/jsonlab/jsonget.m b/Utils/Shared/jsonlab/jsonget.m similarity index 100% rename from Utils/jsonlab/jsonget.m rename to Utils/Shared/jsonlab/jsonget.m diff --git a/Utils/jsonlab/jsonopt.m b/Utils/Shared/jsonlab/jsonopt.m similarity index 100% rename from Utils/jsonlab/jsonopt.m rename to Utils/Shared/jsonlab/jsonopt.m diff --git a/Utils/jsonlab/jsonset.m b/Utils/Shared/jsonlab/jsonset.m similarity index 100% rename from Utils/jsonlab/jsonset.m rename to Utils/Shared/jsonlab/jsonset.m diff --git a/Utils/jsonlab/loadbj.m b/Utils/Shared/jsonlab/loadbj.m similarity index 100% rename from Utils/jsonlab/loadbj.m rename to Utils/Shared/jsonlab/loadbj.m diff --git a/Utils/jsonlab/loadjd.m b/Utils/Shared/jsonlab/loadjd.m similarity index 100% rename from Utils/jsonlab/loadjd.m rename to Utils/Shared/jsonlab/loadjd.m diff --git a/Utils/jsonlab/loadjson.m b/Utils/Shared/jsonlab/loadjson.m similarity index 100% rename from Utils/jsonlab/loadjson.m rename to Utils/Shared/jsonlab/loadjson.m diff --git a/Utils/jsonlab/loadmsgpack.m b/Utils/Shared/jsonlab/loadmsgpack.m similarity index 100% rename from Utils/jsonlab/loadmsgpack.m rename to Utils/Shared/jsonlab/loadmsgpack.m diff --git a/Utils/jsonlab/loadubjson.m b/Utils/Shared/jsonlab/loadubjson.m similarity index 100% rename from Utils/jsonlab/loadubjson.m rename to Utils/Shared/jsonlab/loadubjson.m diff --git a/Utils/jsonlab/lz4decode.m b/Utils/Shared/jsonlab/lz4decode.m similarity index 100% rename from Utils/jsonlab/lz4decode.m rename to Utils/Shared/jsonlab/lz4decode.m diff --git a/Utils/jsonlab/lz4encode.m b/Utils/Shared/jsonlab/lz4encode.m similarity index 100% rename from Utils/jsonlab/lz4encode.m rename to Utils/Shared/jsonlab/lz4encode.m diff --git a/Utils/jsonlab/lz4hcdecode.m b/Utils/Shared/jsonlab/lz4hcdecode.m similarity index 100% rename from Utils/jsonlab/lz4hcdecode.m rename to Utils/Shared/jsonlab/lz4hcdecode.m diff --git a/Utils/jsonlab/lz4hcencode.m b/Utils/Shared/jsonlab/lz4hcencode.m similarity index 100% rename from Utils/jsonlab/lz4hcencode.m rename to Utils/Shared/jsonlab/lz4hcencode.m diff --git a/Utils/jsonlab/lzipdecode.m b/Utils/Shared/jsonlab/lzipdecode.m similarity index 100% rename from Utils/jsonlab/lzipdecode.m rename to Utils/Shared/jsonlab/lzipdecode.m diff --git a/Utils/jsonlab/lzipencode.m b/Utils/Shared/jsonlab/lzipencode.m similarity index 100% rename from Utils/jsonlab/lzipencode.m rename to Utils/Shared/jsonlab/lzipencode.m diff --git a/Utils/jsonlab/lzmadecode.m b/Utils/Shared/jsonlab/lzmadecode.m similarity index 100% rename from Utils/jsonlab/lzmadecode.m rename to Utils/Shared/jsonlab/lzmadecode.m diff --git a/Utils/jsonlab/lzmaencode.m b/Utils/Shared/jsonlab/lzmaencode.m similarity index 100% rename from Utils/jsonlab/lzmaencode.m rename to Utils/Shared/jsonlab/lzmaencode.m diff --git a/Utils/jsonlab/match_bracket.m b/Utils/Shared/jsonlab/match_bracket.m similarity index 100% rename from Utils/jsonlab/match_bracket.m rename to Utils/Shared/jsonlab/match_bracket.m diff --git a/Utils/jsonlab/mergestruct.m b/Utils/Shared/jsonlab/mergestruct.m similarity index 100% rename from Utils/jsonlab/mergestruct.m rename to Utils/Shared/jsonlab/mergestruct.m diff --git a/Utils/jsonlab/nestbracket2dim.m b/Utils/Shared/jsonlab/nestbracket2dim.m similarity index 100% rename from Utils/jsonlab/nestbracket2dim.m rename to Utils/Shared/jsonlab/nestbracket2dim.m diff --git a/Utils/jsonlab/savebj.m b/Utils/Shared/jsonlab/savebj.m similarity index 100% rename from Utils/jsonlab/savebj.m rename to Utils/Shared/jsonlab/savebj.m diff --git a/Utils/jsonlab/savejd.m b/Utils/Shared/jsonlab/savejd.m similarity index 100% rename from Utils/jsonlab/savejd.m rename to Utils/Shared/jsonlab/savejd.m diff --git a/Utils/jsonlab/savejson.m b/Utils/Shared/jsonlab/savejson.m similarity index 100% rename from Utils/jsonlab/savejson.m rename to Utils/Shared/jsonlab/savejson.m diff --git a/Utils/jsonlab/savemsgpack.m b/Utils/Shared/jsonlab/savemsgpack.m similarity index 100% rename from Utils/jsonlab/savemsgpack.m rename to Utils/Shared/jsonlab/savemsgpack.m diff --git a/Utils/jsonlab/saveubjson.m b/Utils/Shared/jsonlab/saveubjson.m similarity index 100% rename from Utils/jsonlab/saveubjson.m rename to Utils/Shared/jsonlab/saveubjson.m diff --git a/Utils/jsonlab/varargin2struct.m b/Utils/Shared/jsonlab/varargin2struct.m similarity index 100% rename from Utils/jsonlab/varargin2struct.m rename to Utils/Shared/jsonlab/varargin2struct.m diff --git a/Utils/jsonlab/zlibdecode.m b/Utils/Shared/jsonlab/zlibdecode.m similarity index 100% rename from Utils/jsonlab/zlibdecode.m rename to Utils/Shared/jsonlab/zlibdecode.m diff --git a/Utils/jsonlab/zlibencode.m b/Utils/Shared/jsonlab/zlibencode.m similarity index 100% rename from Utils/jsonlab/zlibencode.m rename to Utils/Shared/jsonlab/zlibencode.m diff --git a/Utils/Shared/pretty_print_matrix.m b/Utils/Shared/pretty_print_matrix.m new file mode 100644 index 00000000..0c1722a2 --- /dev/null +++ b/Utils/Shared/pretty_print_matrix.m @@ -0,0 +1,31 @@ +function pretty_print_matrix(M, indentrow, fmt) + +if ~exist('indentrow','var') + indentrow = 0; +end +if ~exist('fmt','var') + fmt = sprintf('%%0.4f'); +end + +nr = size(M,1); +nc = size(M,2); + +for ii=1:nr + fprintf(blanks(indentrow)); + for jj=1:nc + + if M(ii,jj)>=0 && M(ii,jj)<10 + fprintf([' ', fmt, ' '], M(ii,jj)); + elseif (M(ii,jj)>=10 && M(ii,jj)<100) || (M(ii,jj)<0 && M(ii,jj)>-10) + fprintf([' ', fmt, ' '], M(ii,jj)); + elseif (M(ii,jj)>=100 && M(ii,jj)<1000) || (M(ii,jj)<=-10 && M(ii,jj)>-100) + fprintf([' ', fmt, ' '], M(ii,jj)); + elseif (M(ii,jj)>=1000) || (M(ii,jj)<=-100 && M(ii,jj)>-1000) + fprintf([' ', fmt, ' '], M(ii,jj)); + elseif M(ii,jj)<=-1000 + fprintf([' ', fmt, ' '], M(ii,jj)); + end + + end + fprintf('\n'); +end diff --git a/Utils/Shared/pretty_print_struct.m b/Utils/Shared/pretty_print_struct.m new file mode 100644 index 00000000..bb87eb2a --- /dev/null +++ b/Utils/Shared/pretty_print_struct.m @@ -0,0 +1,45 @@ +function pretty_print_struct(st, indent, option, logger) +spaces = ''; + +if ~exist('st','var') || isempty(st) + return; +end +if ~exist('indent','var') || isempty(indent) + indent = 0; +end +if ~exist('option','var') || isempty(option) + option = 1; +end +if ~exist('logger','var') + logger = []; +end +logger = InitLogger(logger); + +if iswholenum(indent) + spaces = blanks(indent); +elseif ischar(indent) + spaces = blanks(length(indent)); +end + +if isstruct(st) || isobject(st) + s = evalc('disp(st)'); + c = str2cell_fast(s, char(10)); + for ii=1:length(c) + if option==1 + logger.Write(sprintf('%s%s\n', spaces, strtrim_improve(c{ii}))); + elseif option==2 + logger.Write(sprintf('%s%s\n', spaces, c{ii})); + end + end +else + str = ''; + for jj=1:ndims(st) + if jj==1 + str = num2str(size(st,jj)); + else + str = sprintf('%sx%s', str, num2str(size(st,jj))); + end + end + logger.Write(sprintf(' %s: [%s %s]\n', inputname(1), str, class(st))); +end + diff --git a/Utils/submodules/exeShellCmds.m b/Utils/Shared/repoManagement/exeShellCmds.m similarity index 94% rename from Utils/submodules/exeShellCmds.m rename to Utils/Shared/repoManagement/exeShellCmds.m index 95616829..2c2f57dc 100644 --- a/Utils/submodules/exeShellCmds.m +++ b/Utils/Shared/repoManagement/exeShellCmds.m @@ -14,7 +14,7 @@ end for ii = 1:length(cmds) if preview == false - c = str2cell_startup(cmds{ii}, ' '); + c = str2cell(cmds{ii}, ' '); if strcmp(c{1}, 'cd') try cd(c{2}) diff --git a/Utils/submodules/gitRevert.m b/Utils/Shared/repoManagement/gitRevert.m similarity index 57% rename from Utils/submodules/gitRevert.m rename to Utils/Shared/repoManagement/gitRevert.m index 1fb0305f..5b1d88ce 100644 --- a/Utils/submodules/gitRevert.m +++ b/Utils/Shared/repoManagement/gitRevert.m @@ -1,15 +1,18 @@ -function r = gitRevert(repo) +function r = gitRevert(repo, piece) r = -1; if ~exist('repo','var') || isempty(repo) repo = pwd; end -repoFull = filesepStandard_startup(repo,'full'); +if ~exist('piece','var') + piece = ''; +end +repoFull = filesepStandard(repo,'full'); currdir = pwd; ii = 1; cmds{ii,1} = sprintf('cd %s', repoFull); ii = ii+1; -cmds{ii,1} = sprintf('git checkout %s', repoFull); ii = ii+1; -cmds{ii,1} = sprintf('git clean -fd %s', repoFull); +cmds{ii,1} = sprintf('git checkout %s', [repoFull, piece]); ii = ii+1; +cmds{ii,1} = sprintf('git clean -fd %s', [repoFull, piece]); [errs, msgs] = exeShellCmds(cmds, false, true); diff --git a/Utils/submodules/gitStatus.m b/Utils/Shared/repoManagement/gitStatus.m similarity index 89% rename from Utils/submodules/gitStatus.m rename to Utils/Shared/repoManagement/gitStatus.m index 33e16800..d4ceffd7 100644 --- a/Utils/submodules/gitStatus.m +++ b/Utils/Shared/repoManagement/gitStatus.m @@ -1,4 +1,4 @@ -function [modified, added, deleted, untracked, cmds, errs, msgs] = gitStatus(repo) +function [modified, added, deleted, untracked, cmds, errs, msgs] = gitStatus(repo, piece) modified = {}; added = {}; deleted = {}; @@ -8,12 +8,19 @@ if ~exist('repo','var') || isempty(repo) repo = [pwd, '/']; end +if ~exist('piece','var') + piece = ''; +end currdir = pwd; repoFull = filesepStandard(repo,'full'); ii = 1; cmds{ii,1} = sprintf('cd %s', repoFull); ii = ii+1; -cmds{ii,1} = sprintf('git status'); ii = ii+1; kk = ii-1; +if isempty(piece) + cmds{ii,1} = sprintf('git status'); ii = ii+1; kk = ii-1; +else + cmds{ii,1} = sprintf('git status %s', piece); ii = ii+1; kk = ii-1; +end cmds{ii,1} = sprintf('cd %s', currdir); [errs, msgs] = exeShellCmds(cmds, false, true); diff --git a/Utils/Shared/repoManagement/hasChanges.m b/Utils/Shared/repoManagement/hasChanges.m new file mode 100644 index 00000000..75c8a4c0 --- /dev/null +++ b/Utils/Shared/repoManagement/hasChanges.m @@ -0,0 +1,33 @@ +function [status, modified, added, deleted, untracked] = hasChanges(repo, piece) +status = 0; +if ~exist('piece','var') + piece = ''; +end +[modified, added, deleted, untracked] = gitStatus(repo, piece); +for ii = 1:length(modified) + c = str2cell(modified{ii}, ' '); + if isempty(strfind(c{2}, '..')) + status = 1; + end +end +for ii = 1:length(added) + c = str2cell(added{ii}, ' '); + if isempty(strfind(c{2}, '..')) + status = 1; + end +end +for ii = 1:length(deleted) + c = str2cell(deleted{ii}, ' '); + if isempty(strfind(c{2}, '..')) + status = 1; + end +end +for ii = 1:length(untracked) + c = str2cell(untracked{ii}, ' '); + if isempty(strfind(c{2}, '..')) + status = 1; + end +end + + + diff --git a/Utils/Shared/repoManagement/incrementVersion.m b/Utils/Shared/repoManagement/incrementVersion.m new file mode 100644 index 00000000..8c40a6d9 --- /dev/null +++ b/Utils/Shared/repoManagement/incrementVersion.m @@ -0,0 +1,30 @@ +function n = incrementVersion(n, changelevel) +switch(changelevel) + case 'majormajor' + n(1) = n(1)+1; + n(2) = 0; + n(3) = 0; + case 'major' + if n(2)<100 + n(2) = n(2)+1; + n(3) = 0; + else + n(1) = n(1)+1; + n(2) = 0; + n(3) = 0; + end + case 'minor' + if n(3)<100 + n(3) = n(3)+1; + elseif n(2) < 100 + n(2) = n(2)+1; + n(3) = 0; + else + n(1) = n(2)+1; + n(2) = 0; + n(3) = 0; + end +end + + + diff --git a/Utils/Shared/repoManagement/updateVersions.m b/Utils/Shared/repoManagement/updateVersions.m new file mode 100644 index 00000000..2d103ace --- /dev/null +++ b/Utils/Shared/repoManagement/updateVersions.m @@ -0,0 +1,126 @@ +function [status, versold, versnew, apps, appdirs] = updateVersions(changelevel, appname) +% +% Syntax: +% [status, versold, versnew, apps, appdirs] = updateVersions(changelevel, appname) +% +% Description: +% Update version numbers of main repo and supporting libraries with independent versions +% +% Input: +% changelevel - String specifying the significance of the changes. This determines which number +% first middle or last is incremented. The possible values are +% +% 'majormajor' +% 'major' +% 'minor' +% +% Examples: +% +% [status, versold, versnew] = updateVersions('minor'); +% Utils: v1.1.2 --> v1.1.3 +% DataTree: v1.6.0 --> v1.6.1 +% FuncRegistry: no changes +% homer3: v1.72.0 --> v1.72.1 +% +% +% [status, versold, versnew] = updateVersions('major'); +% Utils: v1.1.2 --> v1.2.0 +% DataTree: v1.6.0 --> v1.7.0 +% FuncRegistry: no changes +% homer3: v1.72.0 --> v1.73.0 +% +% +% [status, versold, versnew] = updateVersions('major', 'AtlasViewerGUI'); +% Utils: v1.1.2 --> v1.2.0 +% DataTree: v1.6.0 --> v1.7.0 +% FuncRegistry: no changes +% homer3: v1.72.0 --> v1.73.0 +% +% +% +if ~exist('appname','var') || isempty(appname) + appname = getNamespace(); + msg = sprintf('App name missing. Namespace not set to app name. Please provide app name.\n'); + if isempty(appname) + fprintf(msg); + return; + end + if isempty(which([appname, '.m'])) + fprintf(msg); + return + end +end +setNamespace(appname); + +[apps, vers, appdirs] = getVersions(); + +if ~exist('changelevel','var') + changelevel = repmat({'minor'},length(apps),1); +elseif ischar(changelevel) + changelevel = repmat({changelevel},length(apps),1); +end + +versold = vers; +versnew = vers; + +status = zeros(length(apps),1); +for ii = 1:length(apps) + status(ii) = hasChanges(appdirs{ii}); + if status(ii) > 0 + [~, n1] = versionstr2num(versold{ii}); + n1 = incrementVersion(n1, changelevel{ii}); + versnew{ii} = versionnum2str(n1); + fprintf('%s:\t\tv%s --> v%s\n', apps{ii}, versold{ii}, versnew{ii}); + updateVersionFile(appdirs{ii}, versnew{ii}); + else + fprintf('%s:\t\tno changes\n', apps{ii}); + end +end +deleteNamespace(appname); + + + +% -------------------------------------------------------------------------- +function [apps, vers, appdirs] = getVersions() +for kk = 1:2 + [apps, vers, appdirs] = dependencies(); + [v, a] = getVernum(); + apps{end+1} = a; + vers{end+1} = v; + appdirs{end+1} = getAppDir(); + for ii = 1:length(appdirs) + repo = filesepStandard(appdirs{ii}); + versionFile = [repo, 'Version.txt']; + if hasChanges(repo, 'Version.txt') + % fprintf('Resetting %s\n', versionFile); + gitRevert(repo, 'Version.txt'); + end + end +end + + +% -------------------------------------------------------------------------- +function updateVersionFile(repo, vernew) +versionFile = [filesepStandard(repo), 'Version.txt']; +if ~ispathvalid(versionFile,'file') + return; +end +fd = fopen(versionFile, 'w'); +fprintf(fd, vernew); +fclose(fd); + + + + +% -------------------------------------------------------------------------- +function appname = getAppName() +appname = getNamespace(); +if ~isempty(appname) + return +end +pname = which('setpaths.m'); +p = fileparts(pname); +[~, appname] = fileparts(p); +setNamespace(appname); + + diff --git a/Utils/Shared/versionnum2str.m b/Utils/Shared/versionnum2str.m new file mode 100644 index 00000000..851259f9 --- /dev/null +++ b/Utils/Shared/versionnum2str.m @@ -0,0 +1,10 @@ +function verstr = versionnum2str(vernum) +verstr = ''; +for ii = 1:length(vernum) + if isempty(verstr) + verstr = num2str(vernum(ii)); + else + verstr = sprintf('%s.%s', verstr, num2str(vernum(ii))); + end +end + diff --git a/Utils/Shared/versionstr2num.m b/Utils/Shared/versionstr2num.m index 94668581..3b04bdd5 100644 --- a/Utils/Shared/versionstr2num.m +++ b/Utils/Shared/versionstr2num.m @@ -1,9 +1,15 @@ -function n = versionstr2num(s) -n = 0; -c = str2cell(s,'.'); +function [n0, n1] = versionstr2num(s) +n0 = 0; +if iscell(s) + c = s; +else + c = str2cell(s,'.'); +end +n1 = zeros(1,length(c)); m = length(c); b = 100; for ii = 1:length(c) - n = n + (str2num(c{ii}) * b^(m-ii)); + n0 = n0 + (str2num(c{ii}) * b^(m-ii)); + n1(1,ii) = str2num(c{ii}); end diff --git a/Utils/jsonlab/DESCRIPTION b/Utils/jsonlab/DESCRIPTION deleted file mode 100644 index df2790d0..00000000 --- a/Utils/jsonlab/DESCRIPTION +++ /dev/null @@ -1,13 +0,0 @@ -Name: jsonlab -Version: 2.9.8 -Date: 2022-29-04 -Title: A JSON/UBJSON/MessagePack encoder/decoder for MATLAB/Octave -Author: Qianqian Fang -Maintainer: Qianqian Fang -Description: JSONLab is a free and open-source implementation of a JSON/UBJSON/MessagePack - encoder and a decoder in the native MATLAB language. It can be used to convert - a MATLAB data structure (array, struct, cell, struct array and cell array) into - JSON/UBJSON formatted string, or decode a JSON/UBJSON/MessagePack file into - MATLAB data. JSONLab supports both MATLAB and GNU Octave (a free MATLAB clone). -URL: https://neurojson.org/jsonlab -Categories: JSON diff --git a/Utils/jsonlab/INDEX b/Utils/jsonlab/INDEX deleted file mode 100644 index dad5b23f..00000000 --- a/Utils/jsonlab/INDEX +++ /dev/null @@ -1,51 +0,0 @@ -jsonlab >> JSONLab -JSON - loadjson - savejson -UBJSON - loadubjson - saveubjson -MessagePack - loadmsgpack - savemsgpack -JData Workspace - jload - jsave -JData Specificaton - jdatadecode - jdataencode -Interface - loadjd - savejd -JSON Mmap - jsonget - jsonset - filterjsonmmap - getfromjsonpath -Compression and Decompression - base64decode - base64encode - gzipdecode - gzipencode - lz4decode - lz4encode - lz4hcdecode - lz4hcencode - lzipdecode - lzipencode - lzmadecode - lzmaencode - zlibdecode - zlibencode -Helper Functions - decodevarname - encodevarname - fast_match_bracket - find_matching_paren - isoctavemesh - jsonopt - match_bracket - matching_bracket - mergestruct - nestbracket2dim - varargin2struct diff --git a/Utils/jsonlab/README.rst b/Utils/jsonlab/README.rst deleted file mode 100644 index 4e919463..00000000 --- a/Utils/jsonlab/README.rst +++ /dev/null @@ -1,718 +0,0 @@ -.. image:: https://neurojson.org/wiki/upload/neurojson_banner_long.png - -######################################################################################## - JSONLab: compact, portable, robust JSON/binary-JSON encoder/decoder for MATLAB/Octave -######################################################################################## - -* Copyright (c) 2011-2022 Qianqian Fang -* License: BSD or GNU General Public License version 3 (GPL v3), see License*.txt -* Version: 2.9.8 (Micronus Prime - Beta) -* URL: https://neurojson.org/jsonlab -* JData Specification Version: V1 Draft-3 (https://neurojson.org/jdata/draft3) -* Binary JData Specification Version: V1 Draft-2 (https://neurojson.org/bjdata/draft2) -* JSON-Mmap Specification Version: V1 Draft-1 (https://neurojson.org/jsonmmap/draft1) -* Compatibility: MATLAB R2008 or newer, GNU Octave 3.8 or newer -* Acknowledgement: This project is supported by US National Institute of Health (NIH) - grant `U24-NS124027 `_ - -.. image:: https://travis-ci.com/fangq/jsonlab.svg?branch=master - :target: https://travis-ci.com/fangq/jsonlab - -################# -Table of Contents -################# -.. contents:: - :local: - :depth: 3 - -============ -What's New -============ - -We are excited to announce that the JSONLab project, as the official reference library -for both the `JData `_ and `BJData `_ -specifications, is funded by the US National Institute of Health (NIH) as -part of the NeuroJSON project (https://neurojson.org). -The goal of the NeuroJSON project is to develop human-readable, scalable and -future-proof neuroimaging data standards and data sharing services. All data -produced from the NeuroJSON project will be using JSON/Binary JData formats as the -underlying serialization standards and use the lightweight JData specification as -language-independent data annotation standard, all of which have been evolved from -the over a decade development of JSONLab. - -JSONLab v2.9.8 - code named "Micronus - beta" - is the beta-release of the next milestone - -JSONLab v3.0 - containing a number of key feature enhancement and bug fixes. The major -new features include - -1. exporting JSON Memory-Map for rapid disk-map like JSON/binary JSON reading - and writing, implementing `JSON-Mmap Spec v1 Draft 1 `_ -2. supporting JSONPath query to MATLAB data and JSON/binary JSON file and streams, -3. (**breaking**) upgrading the supported BJData spec to `V1 Draft 2 `_ - where the default numerical data byte order changed from Big-Endian to **Little-Endian**, -4. adding initial support to JData `_DataLink_ `_ - decoding to link multiple JSON/binary JSON files - -There have been many major updates added to this release since the previous -release v2.0 in June 2020. A list of the major changes are summarized below -(with key features marked by \*), including the support to BJData Draft-2 specification, -new interface functions ``savejd/loadjd``, and options to use MATLAB/Octave built-in -``jsonencode/jsondecode`` functions. The ``octave-jsonlab`` package has also been -included in the official distributions of Debian Bullseye and Ubuntu 21.04 or newer. - -- 2022-04-19*[2278bb1] stop escaping / to \/ in JSON string, see https://mondotondo.com/2010/12/29/the-solidus-issue/ -- 2022-04-01*[fb711bb] add loadjd and savejd as the unified JSON/binary JSON file interface -- 2022-03-30 [4433a21] improve datalink uri handling to consider : inside uri -- 2022-03-30 [6368409] make datalink URL query more robust -- 2022-03-29 [dd9e9c6] when file suffix is missing, assume JSON feed -- 2022-03-29*[07c58f3] initial support for _DataLink_ of online/local file with JSONPath ref -- 2022-03-29 [897b7ba] fix test for older octave -- 2022-03-20 [bf03eff] force msgpack to use big-endian -- 2022-03-13 [46bbfa9] support empty name key, which is valid in JSON, fix #79 -- 2022-03-12 [9ab040a] increase default float number digits from 10 to 16, fix #78 -- 2022-03-11 [485ea29] update error message on the valid root-level markers -- 2022-02-23 [aa3913e] disable TFN marker in optimized header due to security risk and low benefit -- 2022-02-23 [f2c3223] support SCH{[ markers in optimized container type -- 2022-02-14 [540f95c] add optional preceding whitespace, explain format -- 2022-02-13 [3dfa904] debugged and tested mmap, add mmapinclude and mmapexclude options -- 2022-02-10*[6150ae1] handle uncompressed raw data (only base64 encoded) in jdatadecode -- 2022-02-10 [88a59eb] give a warning when jdatadecode fails, but still return the raw data -- 2022-02-03*[05edb7a] fast reading and writing json data record using mmap and jsonpath -- 2022-02-02*[b0f0ebd] return disk-map or memory-map table in loadjson -- 2022-02-01 [0888218] correct typos and add additional descriptions in README -- 2022-02-01*[03133c7] fix row-major ('formatversion',1.8) ND array storage order, update demo outputs -- 2022-02-01 [5998c70] revert variable name encoding to support unicode strings -- 2022-01-31 [16454e7] test flexible whitespaces in 1D/2D arrays, test mixed array from string -- 2022-01-31*[5c1ef15] accelerate fastarrayparser by 200%! jsonlab_speedtest cuts from 11s to 5.8s -- 2022-01-30 [9b25e20] fix octave 3.8 error on travis, it does not support single -- 2022-01-30 [5898f6e] add octave 5.2 to travis -- 2022-01-30*[2e3344c] [bjdata:breaking] Upgrade ``savebj/loadbj`` to BJData v1-draft 2, use little-endian by default -- 2022-01-30*[2e3344c] [bjdata:breaking] Fix optimized ND array element order (previously used column-major) -- 2022-01-30*[2e3344c] optimize loadjson and loadbj speed -- 2022-01-30*[2e3344c] add 'BuiltinJSON' option for ``savejson/loadjson`` to call ``jsonencode/jsondecode`` -- 2022-01-30*[2e3344c] more robust tests on ND array when parsing JSON numerical array construct -- 2021-06-23 [632531f] fix inconsistency between singlet integer and float values, close #70 -- 2021-06-23 [f7d8226] prevent function calls when parsing array strings using eval, fix #75 -- 2021-06-23 [b1ae5fa] fix #73 as a regression to #22 -- 2021-11-22*[ ] octave-jsonlab is officially in Debian Testing/Bullseye -- 2020-09-29 [d0cb3b8] Fix for loading objects. -- 2020-07-26 [d0fb684] Add travis badge -- 2020-07-25 [708c36c] drop octave 3.2 -- 2020-07-25 [436d84e] debug octave 3.2 -- 2020-07-25 [0ce96ec] remove windows and osx targets from travis-ci -- 2020-07-25 [0d8baa4] fix ruby does not support error on windows -- 2020-07-25*[faa7921] enable travis-ci for jsonlab -- 2020-07-08 [321ab1a] add Debian and Ubuntu installation commands -- 2020-07-08 [e686828] update author info -- 2020-07-08*[ce40fdf] supports ND cell array, fix #66 -- 2020-07-07 [6a8ce93] fix string encoding over 399 characters, close #65 -- 2020-06-14 [5a58faf] fix DESCRIPTION date bug -- 2020-06-14 [9d7e94c] match octave description file and upstream version number -- 2020-06-14 [a5b6170] fix warning about ``lz4encode`` file name - - -Please note that the ``savejson/loadjson`` in both JSONLab v2.0-v3.0 are -compliant with JData Spec Draft 3; the savebj/loadbj`` in JSONLab v3.0 is -compatible to BJData spec Draft 2, which contains breaking feature changes -compared to those in JSONLab v2.0. - -The BJData spec was derived from UBJSON spec Draft 12, with the -following breaking differences: - -- BJData adds 4 new numeric data types: ``uint16 [u]``, ``uint32 [m]``, ``uint64 [M]`` - and ``float16 [h]`` (supported in JSONLab v2.0 or newer) -- BJData supports an optimized ND array container (supported in JSONLab since 2013) -- BJData does not convert ``NaN/Inf/-Inf`` to ``null`` (supported in JSONLab since 2013) -- BJData Draft 2 changes the default byte order to Little-Endian instead of Big-Endian (JSONLab 3.0 or later) -- BJData only permits non-zero-fixed-length data types as the optimized array type, i.e. only ``UiuImlMLhdDC`` are allowed - -To avoid using the new features, one should attach ``'UBJSON',1`` and ``'Endian','B'`` -in the ``savebj`` command as - -.. code-block:: matlab - - savebj('',data,'FileName','myfile.bjd','UBJSON',1, 'Endian','B'); - -To read BJData data files generated by JSONLab v2.0, you should call - -.. code-block:: matlab - - data=loadbj('my_old_data_file.bjd','Endian','B') - -You are strongly encouraged to convert all pre-v2.9 JSONLab generated BJD or .jamm -files using the new format. - - -============ -Introduction -============ - -JSONLab is an open-source JSON/UBJSON/MessagePack encoder and decoder written -completely in the native MATLAB language. It can be used to convert most MATLAB -data structures (array, struct, cell, struct array, cell array, and objects) into -JSON/UBJSON/MessagePack formatted strings and files, or to parse a -JSON/UBJSON/MessagePack file into a MATLAB data structure. JSONLab supports both -MATLAB and `GNU Octave `_ (a free MATLAB clone). - -Compared to other MATLAB/Octave JSON parsers, JSONLab is uniquely lightweight, -ultra-portable, producing dependable outputs across a wide-range of MATLAB -(tested on R2008) and Octave (tested on v3.8) versions. It also uniquely supports -BinaryJData/UBJSON/MessagePack data files as binary-JSON-like formats, designed -for efficiency and flexibility with loss-less binary storage. As a parser written -completely with the native MATLAB language, it is surprisingly fast when reading -small-to-moderate sized JSON files (1-2 MB) with simple hierarchical structures, -and is heavily optimized for reading JSON files containing large N-D arrays -(known as the "fast array parser" in ``loadjson``). - -JSON (`JavaScript Object Notation `_) is a highly portable, -human-readable and `"fat-free" `_ text format -to represent complex and hierarchical data, widely used for data-exchange in applications. -UBJSON (`Universal Binary JSON `_) is a binary JSON format, -designed to specifically address the limitations of JSON, permitting the -storage of binary data with strongly typed data records, resulting in smaller -file sizes and fast encoding and decoding. MessagePack is another binary -JSON-like data format widely used in data exchange in web/native applications. -It is slightly more compact than UBJSON, but is not directly readable compared -to UBJSON. - -We envision that both JSON and its binary counterparts will play important -roles for storage, exchange and interoperation of large-scale scientific data -among the wide-variety of tools. As container-formats, they offer both the -flexibility and generality similar to other more sophisticated formats such -as `HDF5 `_, but are significantly -simpler with a much greater software ecosystem. - -Towards this goal, we have developed the JData Specification (http://github.com/NeuroJSON/jdata) -to standardize serializations of complex scientific data structures, such as -N-D arrays, sparse/complex-valued arrays, trees, maps, tables and graphs using -JSON/binary JSON constructs. The text and binary formatted JData files are -syntactically compatible with JSON/UBJSON formats, and can be readily parsed -using existing JSON and UBJSON parsers. JSONLab is not just a parser and writer -of JSON/UBJSON data files, but one that systematically converts complex scientific -data structures into human-readable and universally supported JSON forms using the -standardized JData data annotations. - - -================ -Installation -================ - -The installation of JSONLab is no different from installing any other -MATLAB toolbox. You only need to download/unzip the JSONLab package -to a folder, and add the folder's path to MATLAB/Octave's path list -by using the following command: - -.. code:: shell - - addpath('/path/to/jsonlab'); - -If you want to add this path permanently, you can type ``pathtool``, -browse to the JSONLab root folder and add to the list, then click "Save". -Then, run ``rehash`` in MATLAB, and type ``which savejson``, if you see an -output, that means JSONLab is installed for MATLAB/Octave. - -If you use MATLAB in a shared environment such as a Linux server, the -best way to add path is to type - -.. code:: shell - - mkdir ~/matlab/ - nano ~/matlab/startup.m - -and type ``addpath('/path/to/jsonlab')`` in this file, save and quit the editor. -MATLAB will execute this file every time it starts. For Octave, the file -you need to edit is ``~/.octaverc``, where ``~`` is your home directory. - -To use the data compression features, please download the ZMat toolbox from -https://github.com/fangq/zmat/releases/latest and follow the instruction to -install ZMat first. The ZMat toolbox is required when compression is used on -MATLAB running in the ``-nojvm`` mode or GNU Octave, or 'lzma/lzip/lz4/lz4hc' -compression methods are specified. ZMat can also compress large arrays that -MATLAB's Java-based compression API does not support. - ----------- -Install JSONLab on Fedora 24 or later ----------- - -JSONLab has been available as an official Fedora package since 2015. You may -install it directly using the below command - -.. code:: shell - - sudo dnf install octave-jsonlab - -To enable data compression/decompression, you need to install ``octave-zmat`` using - -.. code:: shell - - sudo dnf install octave-zmat - -Then open Octave, and type ``pkg load jsonlab`` to enable jsonlab toolbox. - ----------- -Install JSONLab on Debian ----------- - -JSONLab is currently available on Debian Bullseye. To install, you may run - -.. code:: shell - - sudo apt-get install octave-jsonlab - -One can alternatively install ``matlab-jsonlab`` if MATLAB is available. - ----------- -Install JSONLab on Ubuntu ----------- - -JSONLab is currently available on Ubuntu 21.04 or newer as package -`octave-jsonlab`. To install, you may run - -.. code:: shell - - sudo apt-get install octave-jsonlab - -For older Ubuntu releases, one can add the below PPA - -https://launchpad.net/~fangq/+archive/ubuntu/ppa - -To install, please run - -.. code:: shell - - sudo add-apt-repository ppa:fangq/ppa - sudo apt-get update - -to add this PPA, and then use - -.. code:: shell - - sudo apt-get install octave-jsonlab - -to install the toolbox. ``octave-zmat`` will be automatically installed. - ----------- -Install JSONLab on Arch Linux ----------- - -JSONLab is also available on Arch Linux. You may install it using the below command - -.. code:: shell - - sudo pikaur -S jsonlab - -================ -Using JSONLab -================ - -JSONLab provides a pair of functions, ``loadjson`` -- a JSON parser, and ``savejson`` -- -a MATLAB-to-JSON encoder, to read/write the text-based JSON; it also provides -three equivalent pairs -- ``loadbj/savebj`` for binary JData, ``loadubjson/saveubjson`` -for UBJSON and ``loadmsgpack/savemsgpack`` for MessagePack. The ``load*`` functions -for the 3 supported data formats share almost the same input parameter format, -similarly for the 3 ``save*`` functions (``savejson/saveubjson/savemsgpack``). -These encoders and decoders are capable of processing/sharing almost all -data structures supported by MATLAB, thanks to ``jdataencode/jdatadecode`` - -a pair of in-memory data converters translating complex MATLAB data structures -to their easy-to-serialized forms according to the JData specifications. -The detailed help information can be found in the ``Contents.m`` file. - -In JSONLab 2.9.8 and later versions, a unified file loading and saving interface -is provided for JSON, binary JSON and HDF5, including ``loadjd`` and ``savejd`` -for reading and writing below files types: - -- JSON based files: ``.json`, ``.jdt`` (text JData file), ``.jmsh`` (text JMesh file), - ``.jnii`` (text JNIfTI file), ``.jnirs`` (text JSNIRF file) -- BJData based files: ``.bjd`, ``.jdb` (binary JData file), ``.bmsh`` (binary JMesh file), - ``.bnii`` (binary JNIfTI file), ``.bnirs`` (binary JSNIRF file), ``.jamm`` (MATLAB session file) -- UBJSON based files: ``.ubj`` -- MessagePack based files: ``.msgpack`` -- HDF5 based files: ``.h5``, ``.hdf5``, ``.snirf`` (SNIRF fNIRS data files) - require `EasyH5 toolbox `_ - - -In the below section, we provide a few examples on how to us each of the -core functions for encoding/decoding JSON/UBJSON/MessagePack data. - ----------- -savejson.m ----------- - -.. code-block:: matlab - - jsonmesh=struct('MeshNode',[0 0 0;1 0 0;0 1 0;1 1 0;0 0 1;1 0 1;0 1 1;1 1 1],... - 'MeshElem',[1 2 4 8;1 3 4 8;1 2 6 8;1 5 6 8;1 5 7 8;1 3 7 8],... - 'MeshSurf',[1 2 4;1 2 6;1 3 4;1 3 7;1 5 6;1 5 7;... - 2 8 4;2 8 6;3 8 4;3 8 7;5 8 6;5 8 7],... - 'MeshCreator','FangQ','MeshTitle','T6 Cube',... - 'SpecialData',[nan, inf, -inf]); - savejson(jsonmesh) - savejson('jmesh',jsonmesh) - savejson('',jsonmesh,'Compact',1) - savejson('jmesh',jsonmesh,'outputfile.json') - savejson('',jsonmesh,'ArrayIndent',0,'FloatFormat','\t%.5g','FileName','outputfile2.json') - savejson('cpxrand',eye(5)+1i*magic(5)) - savejson('ziparray',eye(10),'Compression','zlib','CompressArraySize',1) - savejson('',jsonmesh,'ArrayToStruct',1) - savejson('',eye(10),'UseArrayShape',1) - ----------- -loadjson.m ----------- - -.. code-block:: matlab - - loadjson('{}') - dat=loadjson('{"obj":{"string":"value","array":[1,2,3]}}') - dat=loadjson(['examples' filesep 'example1.json']) - dat=loadjson(['examples' filesep 'example1.json'],'SimplifyCell',0) - -------------- -savebj.m (saveubjson.m as an alias) -------------- - -.. code-block:: matlab - - a={single(rand(2)), struct('va',1,'vb','string'), 1+2i}; - savebj(a) - savebj('rootname',a,'testdata.ubj') - savebj('zeros',zeros(100),'Compression','gzip') - -------------- -loadbj.m (loadubjson.m as an alias) -------------- - -.. code-block:: matlab - - obj=struct('string','value','array',single([1 2 3]),'empty',[],'magic',uint8(magic(5))); - ubjdata=savebj('obj',obj); - dat=loadbj(ubjdata) - class(dat.obj.array) - isequaln(obj,dat.obj) - dat=loadbj(savebj('',eye(10),'Compression','zlib','CompressArraySize',1)) - ----------- -jdataencode.m ----------- - -.. code-block:: matlab - - jd=jdataencode(struct('a',rand(5)+1i*rand(5),'b',[],'c',sparse(5,5))) - savejson('',jd) - ----------- -jdatadecode.m ----------- - -.. code-block:: matlab - - rawdata=struct('a',rand(5)+1i*rand(5),'b',[],'c',sparse(5,5)); - jd=jdataencode(rawdata) - newjd=jdatadecode(jd) - isequaln(newjd,rawdata) - ---------- -examples ---------- - -Under the ``examples`` folder, you can find several scripts to demonstrate the -basic utilities of JSONLab. Running the ``demo_jsonlab_basic.m`` script, you -will see the conversions from MATLAB data structure to JSON text and backward. -In ``jsonlab_selftest.m``, we load complex JSON files downloaded from the Internet -and validate the ``loadjson/savejson`` functions for regression testing purposes. -Similarly, a ``demo_ubjson_basic.m`` script is provided to test the ``saveubjson`` -and ``loadubjson`` functions for various matlab data structures, and -``demo_msgpack_basic.m`` is for testing ``savemsgpack`` and ``loadmsgpack``. - -Please run these examples and understand how JSONLab works before you use -it to process your data. - ---------- -unit testing ---------- - -Under the ``test`` folder, you can find a script to test individual data types and -inputs using various encoders and decoders. This unit testing script also serves as -a **specification validator** to the JSONLab functions and ensure that the outputs -are compliant to the underlying specifications. - - -================ -Using ``jsave/jload`` to share workspace -================ - -Starting from JSONLab v2.0, we provide a pair of functions, ``jsave/jload`` to store -and retrieve variables from the current workspace, similar to the ``save/load`` -functions in MATLAB and Octave. The files that ``jsave/jload`` reads/writes is by -default a binary JData file with a suffix ``.jamm``. The file size is comparable -(can be smaller if use ``lzma`` compression) to ``.mat`` files. This feature -is currently experimental. - -The main benefits of using .jamm file to share matlab variables include - -* a ``.jamm`` file can be 50% smaller than a ``.mat`` file when using - ``jsave(..., "compression","lzma")``; the only drawback is longer saving time. -* a ``.jamm`` file can be readily read/opened among many programming environments, including - Python, JavaScript, Go, Java etc, where .mat file support is not generally available. - Parsers of ``.jamm`` files are largely compatible with UBJSON's parsers available at - http://ubjson.org/?page_id=48 -* a ``.jamm`` file is quasi-human-readable, one can see the internal data fields - even in a command line, for example using ``strings -n 2 file.jamm | astyle``, - making the binary data easy to be understood, shared and reused. -* ``jsave/jload`` can also use MessagePack and JSON formats as the underlying - data storage format, addressing needs from a diverse set of applications. - MessagePack parsers are readily available at https://msgpack.org/ - ----------- -jsave.m ----------- - -.. code-block:: matlab - - jsave % save the current workspace to jamdata.jamm - jsave mydata.jamm - jsave('mydata.jamm','vars',{'var1','var2'}) - jsave('mydata.jamm','compression','lzma') - jsave('mydata.json','compression','gzip') - ----------- -jload.m ----------- - -.. code-block:: matlab - - jload % load variables from jamdata.jamm to the current workspace - jload mydata.jamm % load variables from mydata.jamm - vars=jload('mydata.jamm','vars',{'var1','var2'}) % return vars.var1, vars.var2 - jload('mydata.jamm','simplifycell',0) - jload('mydata.json') - - -================ -Sharing JSONLab created data files in Python -================ - -Despite the use of portable data annotation defined by the JData Specification, -the output JSON files created by JSONLab are 100% JSON compatible (with -the exception that long strings may be broken into multiple lines for better -readability). Therefore, JSONLab-created JSON files (``.json, .jnii, .jnirs`` etc) -can be readily read and written by nearly all existing JSON parsers, including -the built-in ``json`` module parser in Python. - -However, we strongly recommend one to use a lightweight ``jdata`` module, -developed by the same author, to perform the extra JData encoding and decoding -and convert JSON data directly to convenient Python/Numpy data structures. -The ``jdata`` module can also directly read/write UBJSON/Binary JData outputs -from JSONLab (``.bjd, .ubj, .bnii, .bnirs, .jamm`` etc). Using binary JData -files are expected to produce much smaller file sizes and faster parsing, -while maintaining excellent portability and generality. - -In short, to conveniently read/write data files created by JSONLab into Python, -whether they are JSON based or binary JData/UBJSON based, one just need to download -the below two light-weight python modules: - -* **jdata**: PyPi: https://pypi.org/project/jdata/ ; Github: https://github.com/NeuroJSON/pyjdata -* **bjdata** PyPi: https://pypi.org/project/bjdata/ ; Github: https://github.com/NeuroJSON/pybj - -To install these modules on Python 2.x, please first check if your system has -``pip`` and ``numpy``, if not, please install it by running (using Ubuntu/Debian as example) - -.. code-block:: shell - - sudo apt-get install python-pip python3-pip python-numpy python3-numpy - -After the installation is done, one can then install the ``jdata`` and ``bjdata`` modules by - -.. code-block:: shell - - pip install jdata --user - pip install bjdata --user - -To install these modules for Python 3.x, please replace ``pip`` by ``pip3``. -If one prefers to install these modules globally for all users, simply -execute the above commands using - -.. code-block:: shell - - sudo pip install jdata - sudo pip install bjdata - -The above modules require built-in Python modules ``json`` and NumPy (``numpy``). - -Once the necessary modules are installed, one can type ``python`` (or ``python3``), and run - -.. code-block:: python - - import jdata as jd - import numpy as np - from collections import OrderedDict - - data1=jd.loadt('myfile.json',object_pairs_hook=OrderedDict); - data2=jd.loadb('myfile.ubj',object_pairs_hook=OrderedDict); - data3=jd.loadb('myfile.jamm',object_pairs_hook=OrderedDict); - -where ``jd.loadt()`` function loads a text-based JSON file, performs -JData decoding and converts the enclosed data into Python ``dict``, ``list`` -and ``numpy`` objects. Similarly, ``jd.loadb()`` function loads a binary -JData/UBJSON file and performs similar conversions. One can directly call -``jd.load()`` to open JSONLab (and derived toolboxes such as **jnifti**: -https://github.com/NeuroJSON/jnifti or **jsnirf**: https://github.com/NeuroJSON/jsnirf) -generated files based on their respective file suffix. - -Similarly, the ``jd.savet()``, ``jd.saveb()`` and ``jd.save`` functions -can revert the direction and convert a Python/Numpy object into JData encoded -data structure and store as text-, binary- and suffix-determined output files, -respectively. - -======================= -Known Issues and TODOs -======================= - -JSONLab has several known limitations. We are striving to make it more general -and robust. Hopefully in a few future releases, the limitations become less. - -Here are the known issues: - - * 3D or higher dimensional cell/struct-arrays will be converted to 2D arrays - * When processing names containing multi-byte characters, Octave and MATLAB - can give different field-names; you can use - ``feature('DefaultCharacterSet','latin1')`` in MATLAB to get consistent results - * ``savejson`` can only export the properties from MATLAB classes, but not the methods - * ``saveubjson`` converts a logical array into a ``uint8`` (``[U]``) array - * a special N-D array format, as defined in the JData specification, is - implemented in ``saveubjson``. You may use ``saveubjson(...,'NestArray',1)`` - to create UBJSON Draft-12 compliant files - * ``loadubjson`` can not parse all UBJSON Specification (Draft 12) compliant - files, however, it can parse all UBJSON files produced by ``saveubjson``. - -========================== -Contribution and feedback -========================== - -JSONLab is an open-source project. This means you can not only use it and modify -it as you wish, but also you can contribute your changes back to JSONLab so -that everyone else can enjoy the improvement. For anyone who want to contribute, -please download JSONLab source code from its source code repositories by using the -following command: - - -.. code:: shell - - git clone https://github.com/fangq/jsonlab.git jsonlab - -or browsing the github site at - - https://github.com/fangq/jsonlab - -Please report any bugs or issues to the below URL: - - https://github.com/fangq/jsonlab/issues - -Sometimes, you may find it is necessary to modify JSONLab to achieve your -goals, or attempt to modify JSONLab functions to fix a bug that you have -encountered. If you are happy with your changes and willing to share those -changes to the upstream author, you are recommended to create a pull-request -on github. - -To create a pull-request, you first need to "fork" jsonlab on Github by -clicking on the "fork" button on top-right of JSONLab's github page. Once you forked -jsonlab to your own directory, you should then implement the changes in your -own fork. After thoroughly testing it and you are confident the modification -is complete and effective, you can then click on the "New pull request" -button, and on the left, select fangq/jsonlab as the "base". Then type -in the description of the changes. You are responsible to format the code -updates using the same convention (tab-width: 8, indentation: 4 spaces) as -the upstream code. - -We appreciate any suggestions and feedbacks from you. Please use the following -mailing list to report any questions you may have regarding JSONLab: - - https://github.com/fangq/jsonlab/issues - -(Subscription to the mailing list is needed in order to post messages). - - -========================== -Acknowledgement -========================== - ---------- -loadjson.m ---------- - -The ``loadjson.m`` function was significantly modified from the earlier parsers -(BSD 3-clause licensed) written by the below authors - -* Nedialko Krouchev: http://www.mathworks.com/matlabcentral/fileexchange/25713 - created on 2009/11/02 -* François Glineur: http://www.mathworks.com/matlabcentral/fileexchange/23393 - created on 2009/03/22 -* Joel Feenstra: - http://www.mathworks.com/matlabcentral/fileexchange/20565 - created on 2008/07/03 - ---------- -loadmsgpack.m ---------- - -* Author: Bastian Bechtold -* URL: https://github.com/bastibe/matlab-msgpack/blob/master/parsemsgpack.m -* License: BSD 3-clause license - -Copyright (c) 2014,2016 Bastian Bechtold -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of the copyright holder nor the names of its contributors - may be used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---------- -zlibdecode.m, zlibencode.m, gzipencode.m, gzipdecode.m, base64encode.m, base64decode.m ---------- - -* Author: Kota Yamaguchi -* URL: https://www.mathworks.com/matlabcentral/fileexchange/39526-byte-encoding-utilities -* License: BSD License, see below - -Copyright (c) 2012, Kota Yamaguchi -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Utils/jsonlab/examples/demo_jsonlab_basic.m b/Utils/jsonlab/examples/demo_jsonlab_basic.m deleted file mode 100644 index 4ba6a1c8..00000000 --- a/Utils/jsonlab/examples/demo_jsonlab_basic.m +++ /dev/null @@ -1,413 +0,0 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Demonstration of Basic Utilities of JSONlab -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -rngstate = rand ('state'); -randseed=hex2dec('623F9A9E'); -clear data2json json2data - -if(exist('isequaln')==0) - isequaln=@isequal; -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a simple scalar value \n') -fprintf(1,'%%=================================================\n\n') - -data2json=pi -savejson('',data2json) -json2data=loadjson(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% an empty array \n') -fprintf(1,'%%=================================================\n\n') - -data2json=[] -savejson('',data2json) -json2data=loadjson(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% an ampty string \n') -fprintf(1,'%%=================================================\n\n') - -data2json='' -savejson('emptystr',data2json) -json2data=loadjson(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a simple row vector \n') -fprintf(1,'%%=================================================\n\n') - -data2json=1:3 -savejson('',data2json) -json2data=loadjson(ans) -if(~isequaln(json2data,data2json)) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a simple column vector \n') -fprintf(1,'%%=================================================\n\n') - -data2json=(1:3)' -savejson('',data2json) -json2data=loadjson(ans) -if(~isequaln(json2data,data2json)) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a string array \n') -fprintf(1,'%%=================================================\n\n') - -data2json=['AC';'EG'] -savejson('',data2json) -json2data=loadjson(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a string with escape symbols \n') -fprintf(1,'%%=================================================\n\n') - -data2json=sprintf('AB\tCD\none"two') -savejson('str',data2json) -json2data=loadjson(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a mix-typed cell \n') -fprintf(1,'%%=================================================\n\n') - -data2json={'a',true,[2;3]} -savejson('',data2json) -json2data=loadjson(ans) -if(~isequaln(json2data,data2json)) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a 3-D array in nested array form\n') -fprintf(1,'%%=================================================\n\n') - -data2json=reshape(1:(2*4*6),[2,4,6]); -savejson('',data2json,'NestArray',1) -json2data=loadjson(ans) -if(~isequaln(json2data,data2json)) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a 3-D array in annotated array form\n') -fprintf(1,'%%=================================================\n\n') - -data2json=reshape(1:(2*4*6),[2,4,6]); -savejson('',data2json,'NestArray',0) -json2data=loadjson(ans) -if(~isequaln(json2data,data2json)) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a 4-D array in annotated array form\n') -fprintf(1,'%%=================================================\n\n') - -data2json=reshape(1:(2*4*3*2),[2,4,3,2]); -savejson('',data2json,'NestArray',0) % nestarray for 4-D or above is not working -json2data=loadjson(ans) -if(~isequaln(json2data,data2json)) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a 3-D array in nested array form (JSONLab 1.9)\n') -fprintf(1,'%%=================================================\n\n') - -data2json=reshape(1:(2*4*6),[2,4,6]); -savejson('',data2json,'NestArray',1,'FormatVersion',1.8) -json2data=loadjson(ans,'FormatVersion',1.8) -if(~isequaln(json2data,data2json)) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a 3-D array in annotated array form (JSONLab 1.9 or earlier)\n') -fprintf(1,'%%=================================================\n\n') - -data2json=reshape(1:(2*4*6),[2,4,6]); -savejson('',data2json,'NestArray',0,'FormatVersion',1.8) -json2data=loadjson(ans,'FormatVersion',1.8) -if(~isequaln(json2data,data2json)) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a complex number\n') -fprintf(1,'%%=================================================\n\n') - -data2json=1+2i -savejson('',data2json) -json2data=loadjson(ans) -if(~isequaln(json2data,data2json)) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a complex matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=magic(6); -data2json=data2json(:,1:3)+data2json(:,4:6)*1i -savejson('',data2json) -json2data=loadjson(ans) -if(~isequaln(json2data,data2json)) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% MATLAB special constants\n') -fprintf(1,'%%=================================================\n\n') - -data2json=[NaN Inf -Inf] -savejson('specials',data2json) -json2data=loadjson(ans) -if(~isequaln(json2data.specials,data2json)) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a real sparse matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=sprand(10,10,0.1) -savejson('sparse',data2json,'FloatFormat','%.18g') -json2data=loadjson(ans) -if(~isequaln(json2data.sparse,data2json)) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a complex sparse matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=sprand(10,10,0.1); -data2json=data2json-data2json*1i -savejson('complex_sparse',data2json,'FloatFormat','%.18g') -json2data=loadjson(ans) -if(~isequaln(json2data.complex_sparse,data2json)) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% an all-zero sparse matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=sparse(2,3); -savejson('all_zero_sparse',data2json) -json2data=loadjson(ans) -if(~isequaln(json2data.all_zero_sparse,data2json)) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% an empty sparse matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=sparse([]); -savejson('empty_sparse',data2json) -json2data=loadjson(ans) -if(~isequaln(json2data.empty_sparse,data2json)) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% an empty 0-by-0 real matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=[]; -savejson('empty_0by0_real',data2json) -json2data=loadjson(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% an empty 0-by-3 real matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=zeros(0,3); -savejson('empty_0by3_real',data2json) -json2data=loadjson(ans) -if(~isequaln(json2data.empty_0by3_real,data2json)) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a sparse real column vector\n') -fprintf(1,'%%=================================================\n\n') - -data2json=sparse([0,3,0,1,4]'); -savejson('sparse_column_vector',data2json) -json2data=loadjson(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a sparse complex column vector\n') -fprintf(1,'%%=================================================\n\n') - -data2json=data2json-1i*data2json; -savejson('complex_sparse_column_vector',data2json) -json2data=loadjson(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a sparse real row vector\n') -fprintf(1,'%%=================================================\n\n') - -data2json=sparse([0,3,0,1,4]); -savejson('sparse_row_vector',data2json) -json2data=loadjson(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a sparse complex row vector\n') -fprintf(1,'%%=================================================\n\n') - -data2json=data2json-1i*data2json; -savejson('complex_sparse_row_vector',data2json) -json2data=loadjson(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a structure\n') -fprintf(1,'%%=================================================\n\n') - -data2json=struct('name','Think Different','year',1997,'magic',magic(3),... - 'misfits',[Inf,NaN],'embedded',struct('left',true,'right',false)) -savejson('astruct',data2json,struct('ParseLogical',1)) -json2data=loadjson(ans) -class(json2data.astruct.embedded.left) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a structure array\n') -fprintf(1,'%%=================================================\n\n') - -data2json=struct('name','Nexus Prime','rank',9); -data2json(2)=struct('name','Sentinel Prime','rank',9); -data2json(3)=struct('name','Optimus Prime','rank',9); -savejson('Supreme Commander',data2json) -json2data=loadjson(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a cell array\n') -fprintf(1,'%%=================================================\n\n') - -data2json=cell(3,1); -data2json{1}=struct('buzz',1.1,'rex',1.2,'bo',1.3,'hamm',2.0,'slink',2.1,'potato',2.2,... - 'woody',3.0,'sarge',3.1,'etch',4.0,'lenny',5.0,'squeeze',6.0,'wheezy',7.0); -data2json{2}=struct('Ubuntu',['Kubuntu';'Xubuntu';'Lubuntu']); -data2json{3}=[10.04,10.10,11.04,11.10] -savejson('debian',data2json,struct('FloatFormat','%.2f')) -json2data=loadjson(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% invalid field-name handling\n') -fprintf(1,'%%=================================================\n\n') - -json2data=loadjson('{"ValidName":1, "_InvalidName":2, ":Field:":3, "项目":"绝密"}') - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a function handle\n') -fprintf(1,'%%=================================================\n\n') - -data2json=@(x) x+1 -savejson('handle',data2json) -json2data=loadjson(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a 2D cell array\n') -fprintf(1,'%%=================================================\n\n') - -data2json={{1,{2,3}},{4,5},{6};{7},{8,9},{10}}; -savejson('data2json',data2json) -json2data=loadjson(ans) % only savejson works for cell arrays, loadjson has issues - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a 2D struct array\n') -fprintf(1,'%%=================================================\n\n') - -data2json=repmat(struct('idx',0,'data','structs'),[2,3]) -for i=1:6 - data2json(i).idx=i; -end -savejson('data2json',data2json) -json2data=loadjson(ans) - - -if(exist('datetime')) - fprintf(1,'\n%%=================================================\n') - fprintf(1,'%% datetime object \n') - fprintf(1,'%%=================================================\n\n') - - data2json=datetime({'8 April 2015','9 May 2015'}, 'InputFormat','d MMMM yyyy') - savejson('',data2json) - json2data=loadjson(ans) -end - -if(exist('containers.Map')) - fprintf(1,'\n%%=================================================\n') - fprintf(1,'%% a container.Maps object \n') - fprintf(1,'%%=================================================\n\n') - - data2json=containers.Map({'Andy','William','Om'},{21,21,22}) - savejson('',data2json) - json2data=loadjson(ans) -end - -if(exist('istable')) - fprintf(1,'\n%%=================================================\n') - fprintf(1,'%% a table object \n') - fprintf(1,'%%=================================================\n\n') - - Names={'Andy','William','Om'}'; - Age=[21,21,22]'; - data2json=table(Names,Age) - savejson('table',table(Names,Age)) - json2data=loadjson(ans) -end - -if(exist('bandwidth')) - fprintf(1,'\n%%=================================================\n') - fprintf(1,'%% use _ArrayShape_ \n') - fprintf(1,'%%=================================================\n\n') - - lband=2; - uband=3; - data2json=spdiags(true(8,lband+uband+1),-uband:lband,5,8); - data2json=full(double(data2json)); - data2json(data2json~=0)=find(data2json) - - savejson('',data2json,'usearrayshape',1) - json2data=loadjson(ans,'fullarrayshape',1) - - savejson('',tril(data2json),'usearrayshape',1) - json2data=loadjson(ans,'fullarrayshape',1) - - savejson('',triu(data2json+1i*data2json),'usearrayshape',1) - json2data=loadjson(ans,'fullarrayshape',1) - - savejson('',tril(triu(int8(data2json))),'usearrayshape',1) - json2data=loadjson(ans,'fullarrayshape',1) - - savejson('',data2json(:,1:5)+data2json(:,1:5)','usearrayshape',1) - json2data=loadjson(ans,'fullarrayshape',1) -end - -try - val=zlibencode('test'); - fprintf(1,'\n%%=================================================\n') - fprintf(1,'%% a 2-D array in compressed array format\n') - fprintf(1,'%%=================================================\n\n') - - data2json=eye(10); - data2json(20,1)=1; - savejson('',data2json,'Compression','zlib','CompressArraySize',0) % nestarray for 4-D or above is not working - json2data=loadjson(ans) - if(~isequaln(json2data,data2json)) - warning('conversion does not preserve original data'); - end -catch -end - -rand ('state',rngstate); - diff --git a/Utils/jsonlab/examples/demo_msgpack_basic.m b/Utils/jsonlab/examples/demo_msgpack_basic.m deleted file mode 100644 index 23544dfa..00000000 --- a/Utils/jsonlab/examples/demo_msgpack_basic.m +++ /dev/null @@ -1,353 +0,0 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Demonstration of Basic Utilities of JSONlab -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -rngstate = rand ('state'); -randseed=hex2dec('623F9A9E'); -clear data2json json2data - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a simple scalar value \n') -fprintf(1,'%%=================================================\n\n') - -data2json=pi -savemsgpack('',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% an empty array \n') -fprintf(1,'%%=================================================\n\n') - -data2json=[] -savemsgpack('empty',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% an ampty string \n') -fprintf(1,'%%=================================================\n\n') - -data2json='' -savemsgpack('emptystr',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a simple row vector \n') -fprintf(1,'%%=================================================\n\n') - -data2json=1:3 -savemsgpack('',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a simple column vector \n') -fprintf(1,'%%=================================================\n\n') - -data2json=(1:3)' -savemsgpack('',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a string array \n') -fprintf(1,'%%=================================================\n\n') - -data2json=['AC';'EG'] -savemsgpack('',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a string with escape symbols \n') -fprintf(1,'%%=================================================\n\n') - -data2json=sprintf('AB\tCD\none"two') -savemsgpack('str',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a mix-typed cell \n') -fprintf(1,'%%=================================================\n\n') - -data2json={'a',true,[2;3]} -savemsgpack('',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a 3-D array in nested array form\n') -fprintf(1,'%%=================================================\n\n') - -data2json=reshape(1:(2*4*6),[2,4,6]); -savemsgpack('',data2json,'NestArray',1) -json2data=loadmsgpack(ans) -if(any(json2data(:)~=data2json(:)) || any(size(json2data)~=size(data2json))) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a 3-D array in annotated array form\n') -fprintf(1,'%%=================================================\n\n') - -data2json=reshape(1:(2*4*6),[2,4,6]); -savemsgpack('',data2json,'NestArray',0) -json2data=loadmsgpack(ans) -if(any(json2data(:)~=data2json(:)) || any(size(json2data)~=size(data2json))) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a 4-D array in annotated array form\n') -fprintf(1,'%%=================================================\n\n') - -data2json=reshape(1:(2*4*3*2),[2,4,3,2]); -savemsgpack('',data2json,'NestArray',0) % nestarray for 4-D or above is not working -json2data=loadmsgpack(ans) -if(any(json2data(:)~=data2json(:)) || any(size(json2data)~=size(data2json))) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a complex number\n') -fprintf(1,'%%=================================================\n\n') - -data2json=1+2i -savemsgpack('',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a complex matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=magic(6); -data2json=data2json(:,1:3)+data2json(:,4:6)*1i -savemsgpack('',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% MATLAB special constants\n') -fprintf(1,'%%=================================================\n\n') - -data2json=[NaN Inf -Inf] -savemsgpack('specials',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a real sparse matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=sprand(10,10,0.1) -savemsgpack('sparse',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a complex sparse matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=data2json-data2json*1i -savemsgpack('complex_sparse',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% an all-zero sparse matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=sparse(2,3); -savemsgpack('all_zero_sparse',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% an empty sparse matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=sparse([]); -savemsgpack('empty_sparse',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% an empty 0-by-0 real matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=[]; -savemsgpack('empty_0by0_real',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% an empty 0-by-3 real matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=zeros(0,3); -savemsgpack('empty_0by3_real',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a sparse real column vector\n') -fprintf(1,'%%=================================================\n\n') - -data2json=sparse([0,3,0,1,4]'); -savemsgpack('sparse_column_vector',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a sparse complex column vector\n') -fprintf(1,'%%=================================================\n\n') - -data2json=data2json-1i*data2json; -savemsgpack('complex_sparse_column_vector',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a sparse real row vector\n') -fprintf(1,'%%=================================================\n\n') - -data2json=sparse([0,3,0,1,4]); -savemsgpack('sparse_row_vector',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a sparse complex row vector\n') -fprintf(1,'%%=================================================\n\n') - -data2json=data2json-1i*data2json; -savemsgpack('complex_sparse_row_vector',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a structure\n') -fprintf(1,'%%=================================================\n\n') - -data2json=struct('name','Think Different','year',1997,'magic',magic(3),... - 'misfits',[Inf,NaN],'embedded',struct('left',true,'right',false)) -savemsgpack('astruct',data2json,struct('ParseLogical',1)) -json2data=loadmsgpack(ans) -class(json2data.astruct.embedded.left) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a structure array\n') -fprintf(1,'%%=================================================\n\n') - -data2json=struct('name','Nexus Prime','rank',9); -data2json(2)=struct('name','Sentinel Prime','rank',9); -data2json(3)=struct('name','Optimus Prime','rank',9); -savemsgpack('Supreme Commander',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a cell array\n') -fprintf(1,'%%=================================================\n\n') - -data2json=cell(3,1); -data2json{1}=struct('buzz',1.1,'rex',1.2,'bo',1.3,'hamm',2.0,'slink',2.1,'potato',2.2,... - 'woody',3.0,'sarge',3.1,'etch',4.0,'lenny',5.0,'squeeze',6.0,'wheezy',7.0); -data2json{2}=struct('Ubuntu',['Kubuntu';'Xubuntu';'Lubuntu']); -data2json{3}=[10.04,10.10,11.04,11.10] -savemsgpack('debian',data2json,struct('FloatFormat','%.2f')) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% invalid field-name handling\n') -fprintf(1,'%%=================================================\n\n') - -json2data=loadmsgpack(savemsgpack('',loadjson('{"ValidName":1, "_InvalidName":2, ":Field:":3, "项目":"绝密"}'))) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a function handle\n') -fprintf(1,'%%=================================================\n\n') - -data2json=@(x) x+1 -savemsgpack('handle',data2json) -json2data=loadmsgpack(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a 2D cell array\n') -fprintf(1,'%%=================================================\n\n') - -data2json={{1,{2,3}},{4,5},{6};{7},{8,9},{10}}; -savemsgpack('data2json',data2json) -json2data=loadmsgpack(ans) % only savemsgpack works for cell arrays, loadmsgpack has issues - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a 2D struct array\n') -fprintf(1,'%%=================================================\n\n') - -data2json=repmat(struct('idx',0,'data','structs'),[2,3]) -for i=1:6 - data2json(i).idx=i; -end -savemsgpack('data2json',data2json) -json2data=loadmsgpack(ans) - - -if(exist('datetime')) - fprintf(1,'\n%%=================================================\n') - fprintf(1,'%% datetime object \n') - fprintf(1,'%%=================================================\n\n') - - data2json=datetime({'8 April 2015','9 May 2015'}, 'InputFormat','d MMMM yyyy') - savemsgpack('',data2json) - json2data=loadmsgpack(ans) -end - -if(exist('containers.Map')) - fprintf(1,'\n%%=================================================\n') - fprintf(1,'%% a container.Maps object \n') - fprintf(1,'%%=================================================\n\n') - - data2json=containers.Map({'Andy','William','Om'},{21,21,22}) - savemsgpack('',data2json) - json2data=loadmsgpack(ans) -end - -if(exist('istable')) - fprintf(1,'\n%%=================================================\n') - fprintf(1,'%% a table object \n') - fprintf(1,'%%=================================================\n\n') - - Names={'Andy','William','Om'}'; - Age=[21,21,22]'; - data2json=table(Names,Age) - savemsgpack('table',table(Names,Age)) - json2data=loadmsgpack(ans) -end - -if(exist('bandwidth')) - fprintf(1,'\n%%=================================================\n') - fprintf(1,'%% use _ArrayShape_ \n') - fprintf(1,'%%=================================================\n\n') - - lband=2; - uband=3; - data2json=spdiags(true(8,lband+uband+1),-uband:lband,5,8); - data2json=full(double(data2json)); - data2json(data2json~=0)=find(data2json) - - savemsgpack('',data2json,'usearrayshape',1) - json2data=loadmsgpack(ans,'fullarrayshape',1) - - savemsgpack('',tril(data2json),'usearrayshape',1) - json2data=loadmsgpack(ans,'fullarrayshape',1) - - savemsgpack('',triu(data2json+1i*data2json),'usearrayshape',1) - json2data=loadmsgpack(ans,'fullarrayshape',1) - - savemsgpack('',tril(triu(int8(data2json))),'usearrayshape',1) - json2data=loadmsgpack(ans,'fullarrayshape',1) - - savemsgpack('',data2json(:,1:5)+data2json(:,1:5)','usearrayshape',1) - json2data=loadmsgpack(ans,'fullarrayshape',1) -end - -try - val=zlibencode('test'); - fprintf(1,'\n%%=================================================\n') - fprintf(1,'%% a 2-D array in compressed array format\n') - fprintf(1,'%%=================================================\n\n') - - data2json=eye(10); - data2json(20,1)=1; - savemsgpack('',data2json,'Compression','zlib','CompressArraySize',0) % nestarray for 4-D or above is not working - json2data=loadmsgpack(ans) - if(any(json2data(:)~=data2json(:)) || any(size(json2data)~=size(data2json))) - warning('conversion does not preserve original data'); - end -catch -end - -rand ('state',rngstate); - diff --git a/Utils/jsonlab/examples/demo_ubjson_basic.m b/Utils/jsonlab/examples/demo_ubjson_basic.m deleted file mode 100644 index 1f8a12ae..00000000 --- a/Utils/jsonlab/examples/demo_ubjson_basic.m +++ /dev/null @@ -1,367 +0,0 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Demonstration of Basic Utilities of JSONlab -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -rngstate = rand ('state'); -randseed=hex2dec('623F9A9E'); -clear data2json json2data - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a simple scalar value \n') -fprintf(1,'%%=================================================\n\n') - -data2json=pi -savebj('',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% an empty array \n') -fprintf(1,'%%=================================================\n\n') - -data2json=[] -savebj('empty',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% an ampty string \n') -fprintf(1,'%%=================================================\n\n') - -data2json='' -savebj('emptystr',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a simple row vector \n') -fprintf(1,'%%=================================================\n\n') - -data2json=1:3 -savebj('',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a simple column vector \n') -fprintf(1,'%%=================================================\n\n') - -data2json=(1:3)' -savebj('',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a string array \n') -fprintf(1,'%%=================================================\n\n') - -data2json=['AC';'EG'] -savebj('',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a string with escape symbols \n') -fprintf(1,'%%=================================================\n\n') - -data2json=sprintf('AB\tCD\none"two') -savebj('str',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a mix-typed cell \n') -fprintf(1,'%%=================================================\n\n') - -data2json={'a',true,[2;3]} -savebj('',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a 3-D array in nested array form\n') -fprintf(1,'%%=================================================\n\n') - -data2json=reshape(1:(2*4*6),[2,4,6]); -savebj('',data2json,'NestArray',1) -json2data=loadbj(ans) -% if(any(json2data(:)~=data2json(:)) || any(size(json2data)~=size(data2json))) -% warning('conversion does not preserve original data'); -% end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a 3-D array in annotated array form\n') -fprintf(1,'%%=================================================\n\n') - -data2json=reshape(1:(2*4*6),[2,4,6]); -savebj('',data2json,'NestArray',0) -json2data=loadbj(ans) -if(any(json2data(:)~=data2json(:)) || any(size(json2data)~=size(data2json))) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a 4-D array in annotated array form\n') -fprintf(1,'%%=================================================\n\n') - -data2json=reshape(1:(2*4*3*2),[2,4,3,2]); -savebj('',data2json,'NestArray',0) % nestarray for 4-D or above is not working -json2data=loadbj(ans) -if(any(json2data(:)~=data2json(:)) || any(size(json2data)~=size(data2json))) - warning('conversion does not preserve original data'); -end - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a 3-D array in nested array form (JSONLab 1.9)\n') -fprintf(1,'%%=================================================\n\n') - -data2json=reshape(1:(2*4*6),[2,4,6]); -savebj('',data2json,'NestArray',1,'FormatVersion',1.8) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a 3-D array in annotated array form (JSONLab 1.9 or earlier)\n') -fprintf(1,'%%=================================================\n\n') - -data2json=reshape(1:(2*4*6),[2,4,6]); -savebj('',data2json,'NestArray',0,'FormatVersion',1.8) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a complex number\n') -fprintf(1,'%%=================================================\n\n') - -data2json=1+2i -savebj('',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a complex matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=magic(6); -data2json=data2json(:,1:3)+data2json(:,4:6)*1i -savebj('',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% MATLAB special constants\n') -fprintf(1,'%%=================================================\n\n') - -data2json=[NaN Inf -Inf] -savebj('specials',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a real sparse matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=sprand(10,10,0.1) -savebj('sparse',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a complex sparse matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=data2json-data2json*1i -savebj('complex_sparse',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% an all-zero sparse matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=sparse(2,3); -savebj('all_zero_sparse',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% an empty sparse matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=sparse([]); -savebj('empty_sparse',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% an empty 0-by-0 real matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=[]; -savebj('empty_0by0_real',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% an empty 0-by-3 real matrix\n') -fprintf(1,'%%=================================================\n\n') - -data2json=zeros(0,3); -savebj('empty_0by3_real',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a sparse real column vector\n') -fprintf(1,'%%=================================================\n\n') - -data2json=sparse([0,3,0,1,4]'); -savebj('sparse_column_vector',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a sparse complex column vector\n') -fprintf(1,'%%=================================================\n\n') - -data2json=data2json-1i*data2json; -savebj('complex_sparse_column_vector',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a sparse real row vector\n') -fprintf(1,'%%=================================================\n\n') - -data2json=sparse([0,3,0,1,4]); -savebj('sparse_row_vector',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a sparse complex row vector\n') -fprintf(1,'%%=================================================\n\n') - -data2json=data2json-1i*data2json; -savebj('complex_sparse_row_vector',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a structure\n') -fprintf(1,'%%=================================================\n\n') - -data2json=struct('name','Think Different','year',1997,'magic',magic(3),... - 'misfits',[Inf,NaN],'embedded',struct('left',true,'right',false)) -savebj('astruct',data2json,struct('ParseLogical',1)) -json2data=loadbj(ans) -class(json2data.astruct.embedded.left) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a structure array\n') -fprintf(1,'%%=================================================\n\n') - -data2json=struct('name','Nexus Prime','rank',9); -data2json(2)=struct('name','Sentinel Prime','rank',9); -data2json(3)=struct('name','Optimus Prime','rank',9); -savebj('Supreme Commander',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a cell array\n') -fprintf(1,'%%=================================================\n\n') - -data2json=cell(3,1); -data2json{1}=struct('buzz',1.1,'rex',1.2,'bo',1.3,'hamm',2.0,'slink',2.1,'potato',2.2,... - 'woody',3.0,'sarge',3.1,'etch',4.0,'lenny',5.0,'squeeze',6.0,'wheezy',7.0); -data2json{2}=struct('Ubuntu',['Kubuntu';'Xubuntu';'Lubuntu']); -data2json{3}=[10.04,10.10,11.04,11.10] -savebj('debian',data2json,struct('FloatFormat','%.2f')) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% invalid field-name handling\n') -fprintf(1,'%%=================================================\n\n') - -json2data=loadbj(savebj('',loadjson('{"ValidName":1, "_InvalidName":2, ":Field:":3, "项目":"绝密"}'))) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a function handle\n') -fprintf(1,'%%=================================================\n\n') - -data2json=@(x) x+1 -savebj('handle',data2json) -json2data=loadbj(ans) - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a 2D cell array\n') -fprintf(1,'%%=================================================\n\n') - -data2json={{1,{2,3}},{4,5},{6};{7},{8,9},{10}}; -savebj('data2json',data2json) -json2data=loadbj(ans) % only savebj works for cell arrays, loadbj has issues - -fprintf(1,'\n%%=================================================\n') -fprintf(1,'%% a 2D struct array\n') -fprintf(1,'%%=================================================\n\n') - -data2json=repmat(struct('idx',0,'data','structs'),[2,3]) -for i=1:6 - data2json(i).idx=i; -end -savebj('data2json',data2json) -json2data=loadbj(ans) - - -if(exist('datetime')) - fprintf(1,'\n%%=================================================\n') - fprintf(1,'%% datetime object \n') - fprintf(1,'%%=================================================\n\n') - - data2json=datetime({'8 April 2015','9 May 2015'}, 'InputFormat','d MMMM yyyy') - savebj('',data2json) - json2data=loadbj(ans) -end - -if(exist('containers.Map')) - fprintf(1,'\n%%=================================================\n') - fprintf(1,'%% a container.Maps object \n') - fprintf(1,'%%=================================================\n\n') - - data2json=containers.Map({'Andy','William','Om'},{21,21,22}) - savebj('',data2json) - json2data=loadbj(ans) -end - -if(exist('istable')) - fprintf(1,'\n%%=================================================\n') - fprintf(1,'%% a table object \n') - fprintf(1,'%%=================================================\n\n') - - Names={'Andy','William','Om'}'; - Age=[21,21,22]'; - data2json=table(Names,Age) - savebj('table',table(Names,Age)) - json2data=loadbj(ans) -end - -if(exist('bandwidth')) - fprintf(1,'\n%%=================================================\n') - fprintf(1,'%% use _ArrayShape_ \n') - fprintf(1,'%%=================================================\n\n') - - lband=2; - uband=3; - data2json=spdiags(true(8,lband+uband+1),-uband:lband,5,8); - data2json=full(double(data2json)); - data2json(data2json~=0)=find(data2json) - - savebj('',data2json,'usearrayshape',1) - json2data=loadbj(ans,'fullarrayshape',1) - - savebj('',tril(data2json),'usearrayshape',1) - json2data=loadbj(ans,'fullarrayshape',1) - - savebj('',triu(data2json+1i*data2json),'usearrayshape',1) - json2data=loadbj(ans,'fullarrayshape',1) - - savebj('',tril(triu(int8(data2json))),'usearrayshape',1) - json2data=loadbj(ans,'fullarrayshape',1) - - savebj('',data2json(:,1:5)+data2json(:,1:5)','usearrayshape',1) - json2data=loadbj(ans,'fullarrayshape',1) -end - -try - val=zlibencode('test'); - fprintf(1,'\n%%=================================================\n') - fprintf(1,'%% a 2-D array in compressed array format\n') - fprintf(1,'%%=================================================\n\n') - - data2json=eye(10); - data2json(20,1)=1; - savebj('',data2json,'Compression','zlib','CompressArraySize',0) % nestarray for 4-D or above is not working - json2data=loadbj(ans) - if(any(json2data(:)~=data2json(:)) || any(size(json2data)~=size(data2json))) - warning('conversion does not preserve original data'); - end -catch -end - -rand ('state',rngstate); - diff --git a/Utils/jsonlab/examples/example1.json b/Utils/jsonlab/examples/example1.json deleted file mode 100644 index c02607fd..00000000 --- a/Utils/jsonlab/examples/example1.json +++ /dev/null @@ -1,33 +0,0 @@ - { - "firstName": "John", - "lastName": "Smith", - "age": 25, - "address": - { - "streetAddress": "21 2nd Street\nKAKA\nMAMA\nMoo Moo Moo\nMARY HAD A LITTLE LAMB\n", - "city": "New York", - "state": "NY", - "postalCode": "10021" - }, - "phoneNumber": - [ - { - "type": "home", - "number": "212 555-1234" - }, - { - "type": "fax", - "number": "646 555-4567" - } - ], - "ProcessingStream": - [ - "KAKA1", - "KAKA2", - "KAKA3", - "KAKA4", - "KAKA5", - "KAKA6", - "KAKA7" - ] - } diff --git a/Utils/jsonlab/examples/example2.json b/Utils/jsonlab/examples/example2.json deleted file mode 100644 index eacfbf5e..00000000 --- a/Utils/jsonlab/examples/example2.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "glossary": { - "title": "example glossary", - "GlossDiv": { - "title": "S", - "GlossList": { - "GlossEntry": { - "ID": "SGML", - "SortAs": "SGML", - "GlossTerm": "Standard Generalized Markup Language", - "Acronym": "SGML", - "Abbrev": "ISO 8879:1986", - "GlossDef": { - "para": "A meta-markup language, used to create markup languages such as DocBook.", - "GlossSeeAlso": ["GML", "XML"] - }, - "GlossSee": "markup" - } - } - } - } -} diff --git a/Utils/jsonlab/examples/example3.json b/Utils/jsonlab/examples/example3.json deleted file mode 100644 index f52661bf..00000000 --- a/Utils/jsonlab/examples/example3.json +++ /dev/null @@ -1,11 +0,0 @@ -{"menu": { - "id": "file", - "value": "_&File", - "popup": { - "menuitem": [ - {"value": "_&New", "onclick": "CreateNewDoc(\"'\\\"Untitled\\\"'\")"}, - {"value": "_&Open", "onclick": "OpenDoc()"}, - {"value": "_&Close", "onclick": "CloseDoc()"} - ] - } -}} diff --git a/Utils/jsonlab/examples/example4.json b/Utils/jsonlab/examples/example4.json deleted file mode 100644 index 1872c12f..00000000 --- a/Utils/jsonlab/examples/example4.json +++ /dev/null @@ -1,35 +0,0 @@ -[ - { - "sample" : { - "rho" : 1 - } - }, - { - "sample" : { - "rho" : 2 - } - }, - [ - { - "_ArrayType_" : "double", - "_ArraySize_" : [1,2], - "_ArrayData_" : [1,0] - }, - { - "_ArrayType_" : "double", - "_ArraySize_" : [1,2], - "_ArrayData_" : [1,1] - }, - { - "_ArrayType_" : "double", - "_ArraySize_" : [1,2], - "_ArrayData_" : [1,2] - } - ], - [ - "Paper", - "Scissors", - "Stone" - ], - ["a", "b\\", "c\"","d\\\"","e\"[","f\\\"[","g[\\","h[\\\""] -] diff --git a/Utils/jsonlab/examples/jsonlab_basictest.matlab b/Utils/jsonlab/examples/jsonlab_basictest.matlab deleted file mode 100644 index 072415cf..00000000 --- a/Utils/jsonlab/examples/jsonlab_basictest.matlab +++ /dev/null @@ -1,1468 +0,0 @@ - - < M A T L A B (R) > - Copyright 1984-2016 The MathWorks, Inc. - R2016a (9.0.0.341360) 64-bit (glnxa64) - February 11, 2016 - - -For online documentation, see http://www.mathworks.com/support -For product information, visit www.mathworks.com. - - - Academic License - ->> >> >> >> >> >> >> >> >> >> >> -%================================================= ->> % a simple scalar value ->> %================================================= - ->> >> -data2json = - - 3.1416 - ->> -ans = - -[3.141592654] - - ->> -json2data = - - 3.1416 - ->> >> -%================================================= ->> % an empty array ->> %================================================= - ->> >> -data2json = - - [] - ->> -ans = - -[] - - ->> -json2data = - - Empty cell array: 0-by-1 - ->> >> -%================================================= ->> % an ampty string ->> %================================================= - ->> >> -data2json = - - '' - - ->> -ans = - -{ - "emptystr":"" -} - - ->> -json2data = - - emptystr: [1x0 char] - ->> >> -%================================================= ->> % a simple row vector ->> %================================================= - ->> >> -data2json = - - 1 2 3 - ->> -ans = - -[1,2,3] - - ->> -json2data = - - 1 2 3 - ->> >> >> -%================================================= ->> % a simple column vector ->> %================================================= - ->> >> -data2json = - - 1 - 2 - 3 - ->> -ans = - -[ - [1], - [2], - [3] -] - - ->> -json2data = - - 1 - 2 - 3 - ->> >> >> -%================================================= ->> % a string array ->> %================================================= - ->> >> -data2json = - -AC -EG - ->> -ans = - -[ - "AC", - "EG" -] - - ->> -json2data = - - 'AC' 'EG' - ->> >> -%================================================= ->> % a string with escape symbols ->> %================================================= - ->> >> -data2json = - -AB CD -one"two - ->> -ans = - -{ - "str":"AB\tCD\none\"two" -} - - ->> -json2data = - - str: 'AB CD...' - ->> >> -%================================================= ->> % a mix-typed cell ->> %================================================= - ->> >> -data2json = - - 'a' [1] [2x1 double] - ->> -ans = - -[ - "a", - true, - [ - [2], - [3] - ] -] - - ->> -json2data = - - 'a' [1] [2x1 double] - ->> >> >> -%================================================= ->> % a 3-D array in nested array form ->> %================================================= - ->> >> >> -ans = - -[ - [ - [1,9,17,25,33,41], - [3,11,19,27,35,43], - [5,13,21,29,37,45], - [7,15,23,31,39,47] - ], - [ - [2,10,18,26,34,42], - [4,12,20,28,36,44], - [6,14,22,30,38,46], - [8,16,24,32,40,48] - ] -] - - ->> -json2data(:,:,1) = - - 1 3 5 7 - 2 4 6 8 - - -json2data(:,:,2) = - - 9 11 13 15 - 10 12 14 16 - - -json2data(:,:,3) = - - 17 19 21 23 - 18 20 22 24 - - -json2data(:,:,4) = - - 25 27 29 31 - 26 28 30 32 - - -json2data(:,:,5) = - - 33 35 37 39 - 34 36 38 40 - - -json2data(:,:,6) = - - 41 43 45 47 - 42 44 46 48 - ->> >> >> -%================================================= ->> % a 3-D array in annotated array form ->> %================================================= - ->> >> >> -ans = - -{ - "_ArrayType_":"double", - "_ArraySize_":[2,4,6], - "_ArrayData_":[1,9,17,25,33,41,3,11,19,27,35,43,5,13,21,29,37,45,7,15,23,31,39,47,2,10,18,26,34,42,4,12,20,28,36,44,6,14,22,30,38,46,8,16,24,32,40,48] -} - - ->> -json2data(:,:,1) = - - 1 3 5 7 - 2 4 6 8 - - -json2data(:,:,2) = - - 9 11 13 15 - 10 12 14 16 - - -json2data(:,:,3) = - - 17 19 21 23 - 18 20 22 24 - - -json2data(:,:,4) = - - 25 27 29 31 - 26 28 30 32 - - -json2data(:,:,5) = - - 33 35 37 39 - 34 36 38 40 - - -json2data(:,:,6) = - - 41 43 45 47 - 42 44 46 48 - ->> >> >> -%================================================= ->> % a 4-D array in annotated array form ->> %================================================= - ->> >> >> -ans = - -{ - "_ArrayType_":"double", - "_ArraySize_":[2,4,3,2], - "_ArrayData_":[1,25,9,33,17,41,3,27,11,35,19,43,5,29,13,37,21,45,7,31,15,39,23,47,2,26,10,34,18,42,4,28,12,36,20,44,6,30,14,38,22,46,8,32,16,40,24,48] -} - - ->> -json2data(:,:,1,1) = - - 1 3 5 7 - 2 4 6 8 - - -json2data(:,:,2,1) = - - 9 11 13 15 - 10 12 14 16 - - -json2data(:,:,3,1) = - - 17 19 21 23 - 18 20 22 24 - - -json2data(:,:,1,2) = - - 25 27 29 31 - 26 28 30 32 - - -json2data(:,:,2,2) = - - 33 35 37 39 - 34 36 38 40 - - -json2data(:,:,3,2) = - - 41 43 45 47 - 42 44 46 48 - ->> >> >> -%================================================= ->> % a 3-D array in nested array form (JSONLab 1.9) ->> %================================================= - ->> >> >> -ans = - -[ - [ - [1,2], - [3,4], - [5,6], - [7,8] - ], - [ - [9,10], - [11,12], - [13,14], - [15,16] - ], - [ - [17,18], - [19,20], - [21,22], - [23,24] - ], - [ - [25,26], - [27,28], - [29,30], - [31,32] - ], - [ - [33,34], - [35,36], - [37,38], - [39,40] - ], - [ - [41,42], - [43,44], - [45,46], - [47,48] - ] -] - - ->> -json2data(:,:,1) = - - 1 3 5 7 - 2 4 6 8 - - -json2data(:,:,2) = - - 9 11 13 15 - 10 12 14 16 - - -json2data(:,:,3) = - - 17 19 21 23 - 18 20 22 24 - - -json2data(:,:,4) = - - 25 27 29 31 - 26 28 30 32 - - -json2data(:,:,5) = - - 33 35 37 39 - 34 36 38 40 - - -json2data(:,:,6) = - - 41 43 45 47 - 42 44 46 48 - ->> >> >> -%================================================= ->> % a 3-D array in annotated array form (JSONLab 1.9 or earlier) ->> %================================================= - ->> >> >> -ans = - -{ - "_ArrayType_":"double", - "_ArraySize_":[2,4,6], - "_ArrayData_":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48] -} - - ->> -json2data(:,:,1) = - - 1 3 5 7 - 2 4 6 8 - - -json2data(:,:,2) = - - 9 11 13 15 - 10 12 14 16 - - -json2data(:,:,3) = - - 17 19 21 23 - 18 20 22 24 - - -json2data(:,:,4) = - - 25 27 29 31 - 26 28 30 32 - - -json2data(:,:,5) = - - 33 35 37 39 - 34 36 38 40 - - -json2data(:,:,6) = - - 41 43 45 47 - 42 44 46 48 - ->> >> >> -%================================================= ->> % a complex number ->> %================================================= - ->> >> -data2json = - - 1.0000 + 2.0000i - ->> -ans = - -{ - "_ArrayType_":"double", - "_ArraySize_":[1,1], - "_ArrayIsComplex_":true, - "_ArrayData_":[ - [1], - [2] - ] -} - - ->> -json2data = - - 1.0000 + 2.0000i - ->> >> >> -%================================================= ->> % a complex matrix ->> %================================================= - ->> >> >> -data2json = - - 35.0000 +26.0000i 1.0000 +19.0000i 6.0000 +24.0000i - 3.0000 +21.0000i 32.0000 +23.0000i 7.0000 +25.0000i - 31.0000 +22.0000i 9.0000 +27.0000i 2.0000 +20.0000i - 8.0000 +17.0000i 28.0000 +10.0000i 33.0000 +15.0000i - 30.0000 +12.0000i 5.0000 +14.0000i 34.0000 +16.0000i - 4.0000 +13.0000i 36.0000 +18.0000i 29.0000 +11.0000i - ->> -ans = - -{ - "_ArrayType_":"double", - "_ArraySize_":[6,3], - "_ArrayIsComplex_":true, - "_ArrayData_":[ - [35,1,6,3,32,7,31,9,2,8,28,33,30,5,34,4,36,29], - [26,19,24,21,23,25,22,27,20,17,10,15,12,14,16,13,18,11] - ] -} - - ->> -json2data = - - 35.0000 +26.0000i 1.0000 +19.0000i 6.0000 +24.0000i - 3.0000 +21.0000i 32.0000 +23.0000i 7.0000 +25.0000i - 31.0000 +22.0000i 9.0000 +27.0000i 2.0000 +20.0000i - 8.0000 +17.0000i 28.0000 +10.0000i 33.0000 +15.0000i - 30.0000 +12.0000i 5.0000 +14.0000i 34.0000 +16.0000i - 4.0000 +13.0000i 36.0000 +18.0000i 29.0000 +11.0000i - ->> >> >> -%================================================= ->> % MATLAB special constants ->> %================================================= - ->> >> -data2json = - - NaN Inf -Inf - ->> -ans = - -{ - "specials":["_NaN_","_Inf_","-_Inf_"] -} - - ->> -json2data = - - specials: [NaN Inf -Inf] - ->> >> >> -%================================================= ->> % a real sparse matrix ->> %================================================= - ->> >> -data2json = - - (1,2) 0.6557 - (9,2) 0.7577 - (3,5) 0.8491 - (10,5) 0.7431 - (10,8) 0.3922 - (7,9) 0.6787 - (2,10) 0.0357 - (6,10) 0.9340 - (10,10) 0.6555 - ->> -ans = - -{ - "sparse":{ - "_ArrayType_":"double", - "_ArraySize_":[10,10], - "_ArrayIsSparse_":true, - "_ArrayData_":[ - [1,9,3,10,10,7,2,6,10], - [2,2,5,5,8,9,10,10,10], - [0.655740699156586837,0.757740130578333448,0.849129305868777107,0.743132468124916179,0.392227019534168164,0.678735154857773471,0.0357116785741895537,0.933993247757550549,0.655477890177556644] - ] - } -} - - ->> -json2data = - - sparse: [10x10 double] - ->> >> >> -%================================================= ->> % a complex sparse matrix ->> %================================================= - ->> >> >> -data2json = - - (2,1) 0.6551 - 0.6551i - (1,2) 0.7547 - 0.7547i - (1,4) 0.2760 - 0.2760i - (7,5) 0.4984 - 0.4984i - (8,5) 0.9597 - 0.9597i - (9,5) 0.3404 - 0.3404i - (4,7) 0.1190 - 0.1190i - (1,8) 0.6797 - 0.6797i - (3,8) 0.1626 - 0.1626i - (10,8) 0.5853 - 0.5853i - ->> -ans = - -{ - "complex_sparse":{ - "_ArrayType_":"double", - "_ArraySize_":[10,10], - "_ArrayIsComplex_":true, - "_ArrayIsSparse_":true, - "_ArrayData_":[ - [2,1,1,7,8,9,4,1,3,10], - [1,2,4,5,5,5,7,8,8,8], - [0.655098003973840659,0.754686681982360885,0.276025076998578367,0.498364051982142953,0.959743958516081075,0.340385726666133204,0.118997681558376645,0.679702676853674803,0.162611735194630569,0.585267750979777346], - [-0.655098003973840659,-0.754686681982360885,-0.276025076998578367,-0.498364051982142953,-0.959743958516081075,-0.340385726666133204,-0.118997681558376645,-0.679702676853674803,-0.162611735194630569,-0.585267750979777346] - ] - } -} - - ->> -json2data = - - complex_sparse: [10x10 double] - ->> >> >> -%================================================= ->> % an all-zero sparse matrix ->> %================================================= - ->> >> >> -ans = - -{ - "all_zero_sparse":{ - "_ArrayType_":"double", - "_ArraySize_":[2,3], - "_ArrayIsSparse_":true, - "_ArrayData_":[] - } -} - - ->> -json2data = - - all_zero_sparse: [2x3 double] - ->> >> >> -%================================================= ->> % an empty sparse matrix ->> %================================================= - ->> >> >> -ans = - -{ - "empty_sparse":{ - "_ArrayType_":"double", - "_ArraySize_":[0,0], - "_ArrayIsSparse_":true, - "_ArrayData_":[] - } -} - - ->> -json2data = - - empty_sparse: [] - ->> >> >> -%================================================= ->> % an empty 0-by-0 real matrix ->> %================================================= - ->> >> >> -ans = - -{ - "empty_0by0_real":[] -} - - ->> -json2data = - - empty_0by0_real: {0x1 cell} - ->> >> -%================================================= ->> % an empty 0-by-3 real matrix ->> %================================================= - ->> >> >> -ans = - -{ - "empty_0by3_real":{ - "_ArrayType_":"double", - "_ArraySize_":[0,3], - "_ArrayData_":[] - } -} - - ->> -json2data = - - empty_0by3_real: [0x3 double] - ->> >> >> -%================================================= ->> % a sparse real column vector ->> %================================================= - ->> >> >> -ans = - -{ - "sparse_column_vector":{ - "_ArrayType_":"double", - "_ArraySize_":[5,1], - "_ArrayIsSparse_":true, - "_ArrayData_":[ - [2,4,5], - [3,1,4] - ] - } -} - - ->> -json2data = - - sparse_column_vector: [5x1 double] - ->> >> -%================================================= ->> % a sparse complex column vector ->> %================================================= - ->> >> >> -ans = - -{ - "complex_sparse_column_vector":{ - "_ArrayType_":"double", - "_ArraySize_":[5,1], - "_ArrayIsComplex_":true, - "_ArrayIsSparse_":true, - "_ArrayData_":[ - [2,4,5], - [3,1,4], - [-3,-1,-4] - ] - } -} - - ->> -json2data = - - complex_sparse_column_vector: [5x1 double] - ->> >> -%================================================= ->> % a sparse real row vector ->> %================================================= - ->> >> >> -ans = - -{ - "sparse_row_vector":{ - "_ArrayType_":"double", - "_ArraySize_":[1,5], - "_ArrayIsSparse_":true, - "_ArrayData_":[ - [2,4,5], - [3,1,4] - ] - } -} - - ->> -json2data = - - sparse_row_vector: [0 3 0 1 4] - ->> >> -%================================================= ->> % a sparse complex row vector ->> %================================================= - ->> >> >> -ans = - -{ - "complex_sparse_row_vector":{ - "_ArrayType_":"double", - "_ArraySize_":[1,5], - "_ArrayIsComplex_":true, - "_ArrayIsSparse_":true, - "_ArrayData_":[ - [2,4,5], - [3,1,4], - [-3,-1,-4] - ] - } -} - - ->> -json2data = - - complex_sparse_row_vector: [1x5 double] - ->> >> -%================================================= ->> % a structure ->> %================================================= - ->> >> -data2json = - - name: 'Think Different' - year: 1997 - magic: [3x3 double] - misfits: [Inf NaN] - embedded: [1x1 struct] - ->> -ans = - -{ - "astruct":{ - "name":"Think Different", - "year":1997, - "magic":[ - [8,1,6], - [3,5,7], - [4,9,2] - ], - "misfits":["_Inf_","_NaN_"], - "embedded":{ - "left":true, - "right":false - } - } -} - - ->> -json2data = - - astruct: [1x1 struct] - ->> -ans = - -logical - ->> >> -%================================================= ->> % a structure array ->> %================================================= - ->> >> >> >> >> -ans = - -{ - "Supreme Commander":[ - { - "name":"Nexus Prime", - "rank":9 - }, - { - "name":"Sentinel Prime", - "rank":9 - }, - { - "name":"Optimus Prime", - "rank":9 - } - ] -} - - ->> -json2data = - - Supreme_0x20_Commander: [1x3 struct] - ->> >> -%================================================= ->> % a cell array ->> %================================================= - ->> >> >> >> >> -data2json = - - [1x1 struct] - [1x1 struct] - [1x4 double] - ->> -ans = - -{ - "debian":[ - [ - { - "buzz":1.10, - "rex":1.20, - "bo":1.30, - "hamm":2.00, - "slink":2.10, - "potato":2.20, - "woody":3.00, - "sarge":3.10, - "etch":4.00, - "lenny":5.00, - "squeeze":6.00, - "wheezy":7.00 - } - ], - [ - { - "Ubuntu":[ - "Kubuntu", - "Xubuntu", - "Lubuntu" - ] - } - ], - [ - [10.04,10.10,11.04,11.10] - ] - ] -} - - ->> -json2data = - - debian: {[1x1 struct] [1x1 struct] [10.0400 10.1000 11.0400 11.1000]} - ->> >> -%================================================= ->> % invalid field-name handling ->> %================================================= - ->> >> -json2data = - - ValidName: 1 - x0x5F_InvalidName: 2 - x0x3A_Field_0x3A_: 3 - x0xE9A1B9__0xE79BAE_: '绝密' - ->> >> -%================================================= ->> % a function handle ->> %================================================= - ->> >> -data2json = - - @(x)x+1 - ->> -ans = - -{ - "handle":{ - "function":"@(x)x+1", - "type":"anonymous", - "file":"", - "workspace":[ - { - } - ], - "within_file_path":"__base_function" - } -} - - ->> -json2data = - - handle: [1x1 struct] - ->> >> -%================================================= ->> % a 2D cell array ->> %================================================= - ->> >> >> -ans = - -{ - "data2json":[ - [ - [ - 1, - [ - 2, - 3 - ] - ], - [ - 4, - 5 - ], - [ - 6 - ] - ], - [ - [ - 7 - ], - [ - 8, - 9 - ], - [ - 10 - ] - ] - ] -} - - ->> -json2data = - - data2json: {{1x3 cell} {1x3 cell}} - ->> >> -%================================================= ->> % a 2D struct array ->> %================================================= - ->> >> -data2json = - -2x3 struct array with fields: - - idx - data - ->> >> -ans = - -{ - "data2json":[ - [ - { - "idx":1, - "data":"structs" - }, - { - "idx":2, - "data":"structs" - } - ], - [ - { - "idx":3, - "data":"structs" - }, - { - "idx":4, - "data":"structs" - } - ], - [ - { - "idx":5, - "data":"structs" - }, - { - "idx":6, - "data":"structs" - } - ] - ] -} - - ->> -json2data = - - data2json: [2x3 struct] - ->> >> >> -%================================================= -% datetime object -%================================================= - - -data2json = - - 08-Apr-2015 09-May-2015 - - -ans = - -[ - { - "Format":"dd-MMM-uuuu", - "TimeZone":"", - "Year":2015, - "Month":4, - "Day":8, - "Hour":0, - "Minute":0, - "Second":0, - "SystemTimeZone":"America\/New_York" - }, - { - "Format":"dd-MMM-uuuu", - "TimeZone":"", - "Year":2015, - "Month":5, - "Day":9, - "Hour":0, - "Minute":0, - "Second":0, - "SystemTimeZone":"America\/New_York" - } -] - - - -json2data = - -1x2 struct array with fields: - - Format - TimeZone - Year - Month - Day - Hour - Minute - Second - SystemTimeZone - ->> >> -%================================================= -% a container.Maps object -%================================================= - - -data2json = - - Map with properties: - - Count: 3 - KeyType: char - ValueType: double - - -ans = - -{ - "Andy":21, - "Om":22, - "William":21 -} - - - -json2data = - - Andy: 21 - Om: 22 - William: 21 - ->> >> -%================================================= -% a table object -%================================================= - - -data2json = - - Names Age - _________ ___ - - 'Andy' 21 - 'William' 21 - 'Om' 22 - - -ans = - -{ - "table":{ - "_TableCols_":[ - "Names", - "Age" - ], - "_TableRows_":[], - "_TableRecords_":[ - [ - "Andy", - 21 - ], - [ - "William", - 21 - ], - [ - "Om", - 22 - ] - ] - } -} - - - -json2data = - - table: [3x2 table] - ->> >> -%================================================= -% use _ArrayShape_ -%================================================= - - -data2json = - - 1 6 11 0 0 0 0 0 - 2 7 12 17 0 0 0 0 - 3 8 13 18 23 0 0 0 - 4 9 14 19 24 29 0 0 - 0 10 15 20 25 30 35 0 - - -ans = - -{ - "_ArrayType_":"double", - "_ArraySize_":[5,8], - "_ArrayZipSize_":[6,5], - "_ArrayShape_":[ - "band", - 2, - 3 - ], - "_ArrayData_":[11,17,23,29,35,6,12,18,24,30,1,7,13,19,25,0,2,8,14,20,0,0,3,9,15,0,0,0,4,10] -} - - - -json2data = - - 1 6 11 0 0 0 0 0 - 2 7 12 17 0 0 0 0 - 3 8 13 18 23 0 0 0 - 4 9 14 19 24 29 0 0 - 0 10 15 20 25 30 35 0 - - -ans = - -{ - "_ArrayType_":"double", - "_ArraySize_":[5,8], - "_ArrayZipSize_":[4,5], - "_ArrayShape_":[ - "lowerband", - 3 - ], - "_ArrayData_":[1,7,13,19,25,0,2,8,14,20,0,0,3,9,15,0,0,0,4,10] -} - - - -json2data = - - 1 0 0 0 0 0 0 0 - 2 7 0 0 0 0 0 0 - 3 8 13 0 0 0 0 0 - 4 9 14 19 0 0 0 0 - 0 10 15 20 25 0 0 0 - - -ans = - -{ - "_ArrayType_":"double", - "_ArraySize_":[5,8], - "_ArrayIsComplex_":true, - "_ArrayZipSize_":[2,3,5], - "_ArrayShape_":[ - "upperband", - 2 - ], - "_ArrayData_":[11,17,23,29,35,6,12,18,24,30,1,7,13,19,25,11,17,23,29,35,6,12,18,24,30,1,7,13,19,25] -} - - - -json2data = - - Columns 1 through 4 - - 1.0000 + 1.0000i 6.0000 + 6.0000i 11.0000 +11.0000i 0.0000 + 0.0000i - 0.0000 + 0.0000i 7.0000 + 7.0000i 12.0000 +12.0000i 17.0000 +17.0000i - 0.0000 + 0.0000i 0.0000 + 0.0000i 13.0000 +13.0000i 18.0000 +18.0000i - 0.0000 + 0.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i 19.0000 +19.0000i - 0.0000 + 0.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i - - Columns 5 through 8 - - 0.0000 + 0.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i - 0.0000 + 0.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i - 23.0000 +23.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i - 24.0000 +24.0000i 29.0000 +29.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i - 25.0000 +25.0000i 30.0000 +30.0000i 35.0000 +35.0000i 0.0000 + 0.0000i - - -ans = - -{ - "_ArrayType_":"int8", - "_ArraySize_":[5,8], - "_ArrayShape_":"diag", - "_ArrayData_":[1,7,13,19,25] -} - - - -json2data = - - 1 0 0 0 0 0 0 0 - 0 7 0 0 0 0 0 0 - 0 0 13 0 0 0 0 0 - 0 0 0 19 0 0 0 0 - 0 0 0 0 25 0 0 0 - - -ans = - -{ - "_ArrayType_":"double", - "_ArraySize_":[5,5], - "_ArrayZipSize_":[4,5], - "_ArrayShape_":[ - "lowersymmband", - 3 - ], - "_ArrayData_":[2,14,26,38,50,0,8,20,32,44,0,0,14,26,38,0,0,0,4,10] -} - - - -json2data = - - 2 8 14 4 0 - 8 14 20 26 10 - 14 20 26 32 38 - 4 26 32 38 44 - 0 10 38 44 50 - ->> >> -%================================================= -% a 2-D array in compressed array format -%================================================= - - -ans = - -{ - "_ArrayType_":"double", - "_ArraySize_":[20,10], - "_ArrayZipSize_":[1,200], - "_ArrayZipType_":"zlib", - "_ArrayZipData_":"eJxjYACBD/YMNAGj5o6aO2ruKBgFgwtQL10DAMHODQY= -" -} - - - -json2data = - - 1 0 0 0 0 0 0 0 0 0 - 0 1 0 0 0 0 0 0 0 0 - 0 0 1 0 0 0 0 0 0 0 - 0 0 0 1 0 0 0 0 0 0 - 0 0 0 0 1 0 0 0 0 0 - 0 0 0 0 0 1 0 0 0 0 - 0 0 0 0 0 0 1 0 0 0 - 0 0 0 0 0 0 0 1 0 0 - 0 0 0 0 0 0 0 0 1 0 - 0 0 0 0 0 0 0 0 0 1 - 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 - 1 0 0 0 0 0 0 0 0 0 - ->> >> >> >> \ No newline at end of file diff --git a/Utils/jsonlab/examples/jsonlab_selftest.m b/Utils/jsonlab/examples/jsonlab_selftest.m deleted file mode 100644 index 583e8ef1..00000000 --- a/Utils/jsonlab/examples/jsonlab_selftest.m +++ /dev/null @@ -1,27 +0,0 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Regression Test Unit of loadjson and savejson -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -for i=1:4 - fname=sprintf('example%d.json',i); - if(exist(fname,'file')==0) break; end - fprintf(1,'===============================================\n>> %s\n',fname); - json=savejson('data',loadjson(fname)); - fprintf(1,'%s\n',json); - fprintf(1,'%s\n',savejson('data',loadjson(fname),'Compact',1)); - data=loadjson(json); - savejson('data',data,'selftest.json'); - data=loadjson('selftest.json'); -end - -for i=1:4 - fname=sprintf('example%d.json',i); - if(exist(fname,'file')==0) break; end - fprintf(1,'===============================================\n>> %s\n',fname); - json=savebj('data',loadjson(fname)); - fprintf(1,'%s\n',json); - data=loadbj(json); - savejson('',data); - savebj('data',data,'selftest.ubj'); - data=loadbj('selftest.ubj'); -end diff --git a/Utils/jsonlab/examples/jsonlab_selftest.matlab b/Utils/jsonlab/examples/jsonlab_selftest.matlab deleted file mode 100644 index 5a0d5771..00000000 --- a/Utils/jsonlab/examples/jsonlab_selftest.matlab +++ /dev/null @@ -1,163 +0,0 @@ - - < M A T L A B (R) > - Copyright 1984-2016 The MathWorks, Inc. - R2016a (9.0.0.341360) 64-bit (glnxa64) - February 11, 2016 - - -For online documentation, see http://www.mathworks.com/support -For product information, visit www.mathworks.com. - - - Academic License - ->> >> >> >> >> =============================================== ->> example1.json -{ - "data":{ - "firstName":"John", - "lastName":"Smith", - "age":25, - "address":{ - "streetAddress":"21 2nd Street", - "city":"New York", - "state":"NY", - "postalCode":"10021" - }, - "phoneNumber":[ - { - "type":"home", - "number":"212 555-1234" - }, - { - "type":"fax", - "number":"646 555-4567" - } - ] - } -} - -{"data":{"firstName":"John","lastName":"Smith","age":25,"address":{"streetAddress":"21 2nd Street","city":"New York","state":"NY","postalCode":"10021"},"phoneNumber":[{"type":"home","number":"212 555-1234"},{"type":"fax","number":"646 555-4567"}]}} - -=============================================== ->> example2.json -{ - "data":{ - "glossary":{ - "title":"example glossary", - "GlossDiv":{ - "title":"S", - "GlossList":{ - "GlossEntry":{ - "ID":"SGML", - "SortAs":"SGML", - "GlossTerm":"Standard Generalized Markup Language", - "Acronym":"SGML", - "Abbrev":"ISO 8879:1986", - "GlossDef":{ - "para":"A meta-markup language, used to create markup languages such as DocBook.", - "GlossSeeAlso":[ - "GML", - "XML" - ] - }, - "GlossSee":"markup" - } - } - } - } - } -} - -{"data":{"glossary":{"title":"example glossary","GlossDiv":{"title":"S","GlossList":{"GlossEntry":{"ID":"SGML","SortAs":"SGML","GlossTerm":"Standard Generalized Markup Language","Acronym":"SGML","Abbrev":"ISO 8879:1986","GlossDef":{"para":"A meta-markup language, used to create markup languages such as DocBook.","GlossSeeAlso":["GML","XML"]},"GlossSee":"markup"}}}}}} - -=============================================== ->> example3.json -{ - "data":{ - "menu":{ - "id":"file", - "value":"_&File", - "popup":{ - "menuitem":[ - { - "value":"_&New", - "onclick":"CreateNewDoc(\"'\\\"Untitled\\\"'\")" - }, - { - "value":"_&Open", - "onclick":"OpenDoc()" - }, - { - "value":"_&Close", - "onclick":"CloseDoc()" - } - ] - } - } - } -} - -{"data":{"menu":{"id":"file","value":"_&File","popup":{"menuitem":[{"value":"_&New","onclick":"CreateNewDoc(\"'\\\"Untitled\\\"'\")"},{"value":"_&Open","onclick":"OpenDoc()"},{"value":"_&Close","onclick":"CloseDoc()"}]}}}} - -=============================================== ->> example4.json -{ - "data":[ - { - "sample":{ - "rho":1 - } - }, - { - "sample":{ - "rho":2 - } - }, - [ - [ - [1,0] - ], - [ - [1,1] - ], - [ - [1,2] - ] - ], - [ - "Paper", - "Scissors", - "Stone" - ], - [ - "a", - "b\\", - "c\"", - "d\\\"", - "e\"[", - "f\\\"[", - "g[\\", - "h[\\\"" - ] - ] -} - -{"data":[{"sample":{"rho":1}},{"sample":{"rho":2}},[[[1,0]],[[1,1]],[[1,2]]],["Paper","Scissors","Stone"],["a","b\\","c\"","d\\\"","e\"[","f\\\"[","g[\\","h[\\\""]]} - ->> >> =============================================== ->> example1.json -{Udata{U firstNameSUJohnUlastNameSUSmithUageUUaddress{U streetAddressSU 21 2nd StreetUcitySUNew YorkUstateSUNYU -postalCodeSU10021}U phoneNumber[{UtypeSUhomeUnumberSU 212 555-1234}{UtypeSUfaxUnumberSU 646 555-4567}]}} -=============================================== ->> example2.json -{Udata{Uglossary{UtitleSUexample glossaryUGlossDiv{UtitleCSU GlossList{U -GlossEntry{UIDSUSGMLUSortAsSUSGMLU GlossTermSU$Standard Generalized Markup LanguageUAcronymSUSGMLUAbbrevSU ISO 8879:1986UGlossDef{UparaSUHA meta-markup language, used to create markup languages such as DocBook.U GlossSeeAlso[SUGMLSUXML]}UGlossSeeSUmarkup}}}}}} -=============================================== ->> example3.json -{Udata{Umenu{UidSUfileUvalueSU_&FileUpopup{Umenuitem[{UvalueSU_&NewUonclickSUCreateNewDoc("'\"Untitled\"'")}{UvalueSU_&OpenUonclickSU OpenDoc()}{UvalueSU_&CloseUonclickSU -CloseDoc()}]}}}} -=============================================== ->> example4.json -{Udata[{Usample{UrhoU}}{Usample{UrhoU}}[[[$U#U [$U#U[$U#U]][SUPaperSUScissorsSUStone][CaSUb\SUc"SUd\"SUe"[SUf\"[SUg[\SUh[\"]]} ->> \ No newline at end of file diff --git a/Utils/jsonlab/examples/jsonlab_speedtest.m b/Utils/jsonlab/examples/jsonlab_speedtest.m deleted file mode 100644 index 4990fba0..00000000 --- a/Utils/jsonlab/examples/jsonlab_speedtest.m +++ /dev/null @@ -1,21 +0,0 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Benchmarking processing speed of savejson and loadjson -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -datalen=[1e3 1e4 1e5 1e6]; -len=length(datalen); -tsave=zeros(len,1); -tload=zeros(len,1); -for i=1:len - tic; - json=savejson('data',struct('d1',rand(datalen(i),3),'d2',rand(datalen(i),3)>0.5)); - tsave(i)=toc; - data=loadjson(json); - tload(i)=toc-tsave(i); - fprintf(1,'matrix size: %d\n',datalen(i)); -end - -loglog(datalen,tsave,'o-',datalen,tload,'r*-'); -legend('savejson runtime (s)','loadjson runtime (s)'); -xlabel('array size'); -ylabel('running time (s)'); diff --git a/Utils/jsonlab/examples/jsonlab_ubjson_basictest.matlab b/Utils/jsonlab/examples/jsonlab_ubjson_basictest.matlab deleted file mode 100644 index 4ba3c447..00000000 --- a/Utils/jsonlab/examples/jsonlab_ubjson_basictest.matlab +++ /dev/null @@ -1,937 +0,0 @@ - - < M A T L A B (R) > - Copyright 1984-2016 The MathWorks, Inc. - R2016a (9.0.0.341360) 64-bit (glnxa64) - February 11, 2016 - - -For online documentation, see http://www.mathworks.com/support -For product information, visit www.mathworks.com. - - - Academic License - ->> >> >> >> >> >> >> >> >> -%================================================= ->> % a simple scalar value ->> %================================================= - ->> >> -data2json = - - 3.1416 - ->> -ans = - -D-DTû! @ - ->> -json2data = - - 3.1416 - ->> >> -%================================================= ->> % an empty array ->> %================================================= - ->> >> -data2json = - - [] - ->> -ans = - -{UemptyZ} - ->> -json2data = - - empty: [] - ->> >> -%================================================= ->> % an ampty string ->> %================================================= - ->> >> -data2json = - - '' - - ->> -ans = - -{UemptystrSU } - ->> -json2data = - - emptystr: [1x0 char] - ->> >> -%================================================= ->> % a simple row vector ->> %================================================= - ->> >> -data2json = - - 1 2 3 - ->> -ans = - -[$U#U - ->> -json2data = - - 1 2 3 - ->> >> -%================================================= ->> % a simple column vector ->> %================================================= - ->> >> -data2json = - - 1 - 2 - 3 - ->> -ans = - -[$U#[$U#U - ->> -json2data = - - 1 - 2 - 3 - ->> >> -%================================================= ->> % a string array ->> %================================================= - ->> >> -data2json = - -AC -EG - ->> -ans = - -[SUACSUEG] - ->> -json2data = - - 'AC' 'EG' - ->> >> -%================================================= ->> % a string with escape symbols ->> %================================================= - ->> >> -data2json = - -AB CD -one"two - ->> -ans = - -{UstrSU AB CD -one"two} - ->> -json2data = - - str: 'AB CD...' - ->> >> -%================================================= ->> % a mix-typed cell ->> %================================================= - ->> >> -data2json = - - 'a' [1] [2x1 double] - ->> -ans = - -[CaT[$U#[$U#U] - ->> -json2data = - - 'a' [1] [2x1 uint8] - ->> >> -%================================================= ->> % a 3-D array in nested array form ->> %================================================= - ->> >> >> -ans = - -[[[UU UUU!U)][UU UUU#U+][UU UUU%U-][UUUUU'U/]][[UU -UUU"U*][UU UUU$U,][UUUUU&U.][UUUU U(U0]]] - ->> -json2data(:,:,1) = - - 1 3 5 7 - 2 4 6 8 - - -json2data(:,:,2) = - - 9 11 13 15 - 10 12 14 16 - - -json2data(:,:,3) = - - 17 19 21 23 - 18 20 22 24 - - -json2data(:,:,4) = - - 25 27 29 31 - 26 28 30 32 - - -json2data(:,:,5) = - - 33 35 37 39 - 34 36 38 40 - - -json2data(:,:,6) = - - 41 43 45 47 - 42 44 46 48 - ->> >> >> >> >> -%================================================= ->> % a 3-D array in annotated array form ->> %================================================= - ->> >> >> -ans = - -{U _ArrayType_SUdoubleU _ArraySize_[$U#UU _ArrayData_[$U#U0 !) #+ %-'/ -"* $,&. (0} - ->> -json2data(:,:,1) = - - 1 3 5 7 - 2 4 6 8 - - -json2data(:,:,2) = - - 9 11 13 15 - 10 12 14 16 - - -json2data(:,:,3) = - - 17 19 21 23 - 18 20 22 24 - - -json2data(:,:,4) = - - 25 27 29 31 - 26 28 30 32 - - -json2data(:,:,5) = - - 33 35 37 39 - 34 36 38 40 - - -json2data(:,:,6) = - - 41 43 45 47 - 42 44 46 48 - ->> >> >> -%================================================= ->> % a 4-D array in annotated array form ->> %================================================= - ->> >> >> -ans = - -{U _ArrayType_SUdoubleU _ArraySize_[$U#UU _ArrayData_[$U#U0 !) #+ %-'/ -"* $,&. (0} - ->> -json2data(:,:,1,1) = - - 1 3 5 7 - 2 4 6 8 - - -json2data(:,:,2,1) = - - 9 11 13 15 - 10 12 14 16 - - -json2data(:,:,3,1) = - - 17 19 21 23 - 18 20 22 24 - - -json2data(:,:,1,2) = - - 25 27 29 31 - 26 28 30 32 - - -json2data(:,:,2,2) = - - 33 35 37 39 - 34 36 38 40 - - -json2data(:,:,3,2) = - - 41 43 45 47 - 42 44 46 48 - ->> >> >> -%================================================= ->> % a 3-D array in nested array form (JSONLab 1.9) ->> %================================================= - ->> >> >> -ans = - -[[[UU][UU][UU][UU]][[U U -][U U ][U U][UU]][[UU][UU][UU][UU]][[UU][UU][UU][UU ]][[U!U"][U#U$][U%U&][U'U(]][[U)U*][U+U,][U-U.][U/U0]]] - ->> >> -%================================================= ->> % a 3-D array in annotated array form (JSONLab 1.9 or earlier) ->> %================================================= - ->> >> >> -ans = - -{U _ArrayType_SUdoubleU _ArraySize_[$U#UU _ArrayData_[$U#U0 -  !"#$%&'()*+,-./0} - ->> >> -%================================================= ->> % a complex number ->> %================================================= - ->> >> -data2json = - - 1.0000 + 2.0000i - ->> -ans = - -{U _ArrayType_SUdoubleU _ArraySize_[$U#UU_ArrayIsComplex_TU _ArrayData_[$U#[$U#U} - ->> -json2data = - - 1.0000 + 2.0000i - ->> >> -%================================================= ->> % a complex matrix ->> %================================================= - ->> >> >> -data2json = - - 35.0000 +26.0000i 1.0000 +19.0000i 6.0000 +24.0000i - 3.0000 +21.0000i 32.0000 +23.0000i 7.0000 +25.0000i - 31.0000 +22.0000i 9.0000 +27.0000i 2.0000 +20.0000i - 8.0000 +17.0000i 28.0000 +10.0000i 33.0000 +15.0000i - 30.0000 +12.0000i 5.0000 +14.0000i 34.0000 +16.0000i - 4.0000 +13.0000i 36.0000 +18.0000i 29.0000 +11.0000i - ->> -ans = - -{U _ArrayType_SUdoubleU _ArraySize_[$U#UU_ArrayIsComplex_TU _ArrayData_[$U#[$U#U#  !"$ -   } - ->> -json2data = - - 35.0000 +26.0000i 1.0000 +19.0000i 6.0000 +24.0000i - 3.0000 +21.0000i 32.0000 +23.0000i 7.0000 +25.0000i - 31.0000 +22.0000i 9.0000 +27.0000i 2.0000 +20.0000i - 8.0000 +17.0000i 28.0000 +10.0000i 33.0000 +15.0000i - 30.0000 +12.0000i 5.0000 +14.0000i 34.0000 +16.0000i - 4.0000 +13.0000i 36.0000 +18.0000i 29.0000 +11.0000i - ->> >> -%================================================= ->> % MATLAB special constants ->> %================================================= - ->> >> -data2json = - - NaN Inf -Inf - ->> -ans = - -{Uspecials[$D#U øÿ ð ðÿ} - ->> -json2data = - - specials: [NaN Inf -Inf] - ->> >> -%================================================= ->> % a real sparse matrix ->> %================================================= - ->> >> -data2json = - - (1,2) 0.6557 - (9,2) 0.7577 - (3,5) 0.8491 - (10,5) 0.7431 - (10,8) 0.3922 - (7,9) 0.6787 - (2,10) 0.0357 - (6,10) 0.9340 - (10,10) 0.6555 - ->> -ans = - -{Usparse{U _ArrayType_SUdoubleU _ArraySize_[$U#U - -U_ArrayIsSparse_TU _ArrayData_[$D#[$U#U ð? "@ @ $@ $@ @ @ @ $@ @ @ @ @ @ "@ $@ $@ $@21ëÓûä?;lö:h?è?±Ù8,ë?#'æ½½Çç?€o`[?Ù?éN˜É2¸å?àpÍH¢?P¶¹ÎEãí?¶ ²Ä¬ùä?}} - ->> -json2data = - - sparse: [10x10 double] - ->> >> -%================================================= ->> % a complex sparse matrix ->> %================================================= - ->> >> -data2json = - - (1,2) 0.6557 - 0.6557i - (9,2) 0.7577 - 0.7577i - (3,5) 0.8491 - 0.8491i - (10,5) 0.7431 - 0.7431i - (10,8) 0.3922 - 0.3922i - (7,9) 0.6787 - 0.6787i - (2,10) 0.0357 - 0.0357i - (6,10) 0.9340 - 0.9340i - (10,10) 0.6555 - 0.6555i - ->> -ans = - -{Ucomplex_sparse{U _ArrayType_SUdoubleU _ArraySize_[$U#U - -U_ArrayIsComplex_TU_ArrayIsSparse_TU _ArrayData_[$D#[$U#U ð? "@ @ $@ $@ @ @ @ $@ @ @ @ @ @ "@ $@ $@ $@21ëÓûä?;lö:h?è?±Ù8,ë?#'æ½½Çç?€o`[?Ù?éN˜É2¸å?àpÍH¢?P¶¹ÎEãí?¶ ²Ä¬ùä?21ëÓûä¿;lö:h?迱Ù8,ë¿#'æ½½Ç翀o`[?Ù¿éN˜É2¸å¿àpÍH¢¿P¶¹ÎEãí¿¶ ²Ä¬ùä¿}} - ->> -json2data = - - complex_sparse: [10x10 double] - ->> >> -%================================================= ->> % an all-zero sparse matrix ->> %================================================= - ->> >> >> -ans = - -{Uall_zero_sparse{U _ArrayType_SUdoubleU _ArraySize_[$U#UU_ArrayIsSparse_TU _ArrayData_Z}} - ->> -json2data = - - all_zero_sparse: [2x3 double] - ->> >> -%================================================= ->> % an empty sparse matrix ->> %================================================= - ->> >> >> -ans = - -{U empty_sparseZ} - ->> -json2data = - - empty_sparse: [] - ->> >> -%================================================= ->> % an empty 0-by-0 real matrix ->> %================================================= - ->> >> >> -ans = - -{Uempty_0by0_realZ} - ->> -json2data = - - empty_0by0_real: [] - ->> >> -%================================================= ->> % an empty 0-by-3 real matrix ->> %================================================= - ->> >> >> -ans = - -{Uempty_0by3_realZ} - ->> -json2data = - - empty_0by3_real: [] - ->> >> -%================================================= ->> % a sparse real column vector ->> %================================================= - ->> >> >> -ans = - -{Usparse_column_vector{U _ArrayType_SUdoubleU _ArraySize_[$U#UU_ArrayIsSparse_TU _ArrayData_[$U#[$U#U}} - ->> -json2data = - - sparse_column_vector: [5x1 double] - ->> >> -%================================================= ->> % a sparse complex column vector ->> %================================================= - ->> >> >> -ans = - -{Ucomplex_sparse_column_vector{U _ArrayType_SUdoubleU _ArraySize_[$U#UU_ArrayIsComplex_TU_ArrayIsSparse_TU _ArrayData_[$i#[$U#Uýÿü}} - ->> -json2data = - - complex_sparse_column_vector: [5x1 double] - ->> >> -%================================================= ->> % a sparse real row vector ->> %================================================= - ->> >> >> -ans = - -{Usparse_row_vector{U _ArrayType_SUdoubleU _ArraySize_[$U#UU_ArrayIsSparse_TU _ArrayData_[$U#[$U#U}} - ->> -json2data = - - sparse_row_vector: [0 3 0 1 4] - ->> >> -%================================================= ->> % a sparse complex row vector ->> %================================================= - ->> >> >> -ans = - -{Ucomplex_sparse_row_vector{U _ArrayType_SUdoubleU _ArraySize_[$U#UU_ArrayIsComplex_TU_ArrayIsSparse_TU _ArrayData_[$i#[$U#Uýÿü}} - ->> -json2data = - - complex_sparse_row_vector: [1x5 double] - ->> >> -%================================================= ->> % a structure ->> %================================================= - ->> >> -data2json = - - name: 'Think Different' - year: 1997 - magic: [3x3 double] - misfits: [Inf NaN] - embedded: [1x1 struct] - ->> -ans = - -{Uastruct{UnameSUThink DifferentUyearuÍUmagic[$U#[$U#U Umisfits[$D#U ð øÿUembedded{UleftTUrightF}}} - ->> -json2data = - - astruct: [1x1 struct] - ->> -ans = - -logical - ->> >> -%================================================= ->> % a structure array ->> %================================================= - ->> >> >> >> >> -ans = - -{USupreme Commander[{UnameSU Nexus PrimeUrankU }{UnameSUSentinel PrimeUrankU }{UnameSU Optimus PrimeUrankU }]} - ->> -json2data = - - Supreme_0x20_Commander: {[1x1 struct] [1x1 struct] [1x1 struct]} - ->> >> -%================================================= ->> % a cell array ->> %================================================= - ->> >> >> >> >> -data2json = - - [1x1 struct] - [1x1 struct] - [1x4 double] - ->> -ans = - -{Udebian[[{UbuzzDš™™™™™ñ?UrexD333333ó?UboDÍÌÌÌÌÌô?UhammUUslinkDÍÌÌÌÌÌ @UpotatoDš™™™™™@UwoodyUUsargeDÍÌÌÌÌÌ@UetchUUlennyUUsqueezeUUwheezyU}{UUbuntu[SUKubuntuSUXubuntuSULubuntu]}[$D#U®Gáz$@333333$@®Gáz&@333333&@]]} - ->> -json2data = - - debian: {{1x3 cell}} - ->> >> -%================================================= ->> % invalid field-name handling ->> %================================================= - ->> >> -json2data = - - ValidName: 1 - x0x5F_InvalidName: 2 - x0x3A_Field_0x3A_: 3 - x0xE9A1B9__0xE79BAE_: '绝密' - ->> >> -%================================================= ->> % a function handle ->> %================================================= - ->> >> -data2json = - - @(x)x+1 - ->> -ans = - -{Uhandle{UfunctionSU@(x)x+1UtypeSU anonymousUfileSU U workspace[{}]Uwithin_file_pathSU__base_function}} - ->> -json2data = - - handle: [1x1 struct] - ->> >> -%================================================= ->> % a 2D cell array ->> %================================================= - ->> >> >> -ans = - -{U data2json[[[U[UU]][UU][U]][[U][UU ][U -]]]} - ->> -json2data = - - data2json: {{1x3 cell} {1x3 cell}} - ->> >> -%================================================= ->> % a 2D struct array ->> %================================================= - ->> >> -data2json = - -2x3 struct array with fields: - - idx - data - ->> >> -ans = - -{U data2json[[{UidxUUdataSUstructs}{UidxUUdataSUstructs}][{UidxUUdataSUstructs}{UidxUUdataSUstructs}][{UidxUUdataSUstructs}{UidxUUdataSUstructs}]]} - ->> -json2data = - - data2json: {{1x2 cell} {1x2 cell} {1x2 cell}} - ->> >> >> -%================================================= -% datetime object -%================================================= - - -data2json = - - 08-Apr-2015 09-May-2015 - - -ans = - -[{UFormatSU dd-MMM-uuuuUTimeZoneSU UYearußUMonthUUDayUUHourU UMinuteU USecondU USystemTimeZoneSUAmerica/New_York}{UFormatSU dd-MMM-uuuuUTimeZoneSU UYearußUMonthUUDayU UHourU UMinuteU USecondU USystemTimeZoneSUAmerica/New_York}] - - -json2data = - - [1x1 struct] [1x1 struct] - ->> >> -%================================================= -% a container.Maps object -%================================================= - - -data2json = - - Map with properties: - - Count: 3 - KeyType: char - ValueType: double - - -ans = - -{UAndyUUOmUUWilliamU} - - -json2data = - - Andy: 21 - Om: 22 - William: 21 - ->> >> -%================================================= -% a table object -%================================================= - - -data2json = - - Names Age - _________ ___ - - 'Andy' 21 - 'William' 21 - 'Om' 22 - - -ans = - -{Utable{U _TableCols_[SUNamesSUAge]U _TableRows_ZU_TableRecords_[[SUAndyU][SUWilliamU][SUOmU]]}} - - -json2data = - - table: [3x2 table] - ->> >> -%================================================= -% use _ArrayShape_ -%================================================= - - -data2json = - - 1 6 11 0 0 0 0 0 - 2 7 12 17 0 0 0 0 - 3 8 13 18 23 0 0 0 - 4 9 14 19 24 29 0 0 - 0 10 15 20 25 30 35 0 - - -ans = - -{U _ArrayType_SUdoubleU _ArraySize_[$U#UU_ArrayZipSize_[$U#UU _ArrayShape_[SUbandUU]U _ArrayData_[$U#U #       -} - - -json2data = - - 1 6 11 0 0 0 0 0 - 2 7 12 17 0 0 0 0 - 3 8 13 18 23 0 0 0 - 4 9 14 19 24 29 0 0 - 0 10 15 20 25 30 35 0 - - -ans = - -{U _ArrayType_SUdoubleU _ArraySize_[$U#UU_ArrayZipSize_[$U#UU _ArrayShape_[SU lowerbandU]U _ArrayData_[$U#U      -} - - -json2data = - - 1 0 0 0 0 0 0 0 - 2 7 0 0 0 0 0 0 - 3 8 13 0 0 0 0 0 - 4 9 14 19 0 0 0 0 - 0 10 15 20 25 0 0 0 - - -ans = - -{U _ArrayType_SUdoubleU _ArraySize_[$U#UU_ArrayIsComplex_TU_ArrayZipSize_[$U#UU _ArrayShape_[SU upperbandU]U _ArrayData_[$U#U #   #  } - - -json2data = - - Columns 1 through 4 - - 1.0000 + 1.0000i 6.0000 + 6.0000i 11.0000 +11.0000i 0.0000 + 0.0000i - 0.0000 + 0.0000i 7.0000 + 7.0000i 12.0000 +12.0000i 17.0000 +17.0000i - 0.0000 + 0.0000i 0.0000 + 0.0000i 13.0000 +13.0000i 18.0000 +18.0000i - 0.0000 + 0.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i 19.0000 +19.0000i - 0.0000 + 0.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i - - Columns 5 through 8 - - 0.0000 + 0.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i - 0.0000 + 0.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i - 23.0000 +23.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i - 24.0000 +24.0000i 29.0000 +29.0000i 0.0000 + 0.0000i 0.0000 + 0.0000i - 25.0000 +25.0000i 30.0000 +30.0000i 35.0000 +35.0000i 0.0000 + 0.0000i - - -ans = - -{U _ArrayType_SUint8U _ArraySize_[$U#UU _ArrayShape_SUdiagU _ArrayData_[$U#U } - - -json2data = - - 1 0 0 0 0 0 0 0 - 0 7 0 0 0 0 0 0 - 0 0 13 0 0 0 0 0 - 0 0 0 19 0 0 0 0 - 0 0 0 0 25 0 0 0 - - -ans = - -{U _ArrayType_SUdoubleU _ArraySize_[$U#UU_ArrayZipSize_[$U#UU _ArrayShape_[SU lowersymmbandU]U _ArrayData_[$U#U&2  , &  -} - - -json2data = - - 2 8 14 4 0 - 8 14 20 26 10 - 14 20 26 32 38 - 4 26 32 38 44 - 0 10 38 44 50 - ->> >> -%================================================= -% a 2-D array in compressed array format -%================================================= - - -ans = - -{U _ArrayType_SUdoubleU _ArraySize_[$U#U -U_ArrayZipSize_[$U#UÈU_ArrayZipType_SUzlibU_ArrayZipData_[$U#U xœc` ö 4£æŽš;jî(ƒ P/] ÁÎ } - - -json2data = - - 1 0 0 0 0 0 0 0 0 0 - 0 1 0 0 0 0 0 0 0 0 - 0 0 1 0 0 0 0 0 0 0 - 0 0 0 1 0 0 0 0 0 0 - 0 0 0 0 1 0 0 0 0 0 - 0 0 0 0 0 1 0 0 0 0 - 0 0 0 0 0 0 1 0 0 0 - 0 0 0 0 0 0 0 1 0 0 - 0 0 0 0 0 0 0 0 1 0 - 0 0 0 0 0 0 0 0 0 1 - 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 - 1 0 0 0 0 0 0 0 0 0 - ->> >> >> >> \ No newline at end of file diff --git a/Utils/jsonlab/examples/random_json_joke.m b/Utils/jsonlab/examples/random_json_joke.m deleted file mode 100644 index 4ee776a6..00000000 --- a/Utils/jsonlab/examples/random_json_joke.m +++ /dev/null @@ -1,29 +0,0 @@ -function jokes=random_json_joke(num, url) -% this example shows how to use the _DataLink_ annotation defined in the -% JData specification -% (https://github.com/NeuroJSON/jdata/blob/master/JData_specification.md#data-referencing-and-links) -% to define linked JSON/binary JSON data using external files or URL on the -% web. In the below example, the jokeapi.dev feed returns a JSON record via -% RESTFul URL, the returned record contains a subfield called `joke`, which -% can be retrieved via the JSONPath $.joke attched after the URL, separated -% by a colon. The general _DataLink_ URL is in the form of "URL:$jsonpath" - -if(nargin==0) - num=1; -end - -if(nargin<2) - url='https://v2.jokeapi.dev/joke/Programming?type=single'; -end - -joke.(encodevarname('_DataLink_'))=[url ':$.joke']; -jurl=savejson('',joke); - -jokes=cell(1,num); -for i=1:num - jokes{i}=loadjson(jurl, 'maxlinklevel',1); -end - -if(num==1) - jokes=jokes{1}; -end \ No newline at end of file diff --git a/Utils/jsonlab/gendocs.sh b/Utils/jsonlab/gendocs.sh deleted file mode 100644 index 02b366de..00000000 --- a/Utils/jsonlab/gendocs.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/sh - -#============================================================ -# JSONLAB inline documentation to wiki convertor -# -# Author: Qianqian Fang -#============================================================ - -print_help() -{ - awk '/^%/ {dp=1} / this file is part of EasyH5/ {exit} \ - /-- this function is part of JSONLab/ {exit} \ - /^function/ {dp=1} /./ {if(dp==1) print;}' $1 \ - | grep -v 'Qianqian' | grep -v 'date:' | #grep -v '^%\s*$'| \ - sed -e 's/^function\(.*$\)/\n%==== function\1 ====/g' -} -print_group() -{ - for fun in $@ - do - print_help $fun.m - done - echo '' -} - -func_jdata="jdataencode jdatadecode" -func_json="loadjson savejson" -func_bjdata="loadbj savebj" -func_ubjson="loadubjson saveubjson" -func_msgpack="loadmsgpack savemsgpack" -func_space="jsave jload" -func_interface="loadjd savejd" -func_mmap="jsonget jsonset getfromjsonpath filterjsonmmap" -func_zip="zlibencode zlibdecode gzipencode gzipdecode lzmaencode lzmadecode - lzipencode lzipdecode lz4encode lz4decode lz4hcencode lz4hcdecode - base64encode base64decode encodevarname decodevarname" -func_helper="jsonopt mergestruct varargin2struct match_bracket - fast_match_bracket nestbracket2dim" - -echo %%=== "#" JData specification === -print_group $func_jdata - -echo %%=== "#" JSON === -print_group $func_json - -echo %%=== "#" BJData === -print_group $func_bjdata - -echo %%=== "#" UBJSON === -print_group $func_ubjson - -echo %%=== "#" MessagePack === -print_group $func_msgpack - -echo %%=== "#" Workspace === -print_group $func_space - -echo %%=== "#" Interface === -print_group $func_interface - -echo %%=== "#" Memory-map === -print_group $func_mmap - -echo %%=== "#" Compression and decompression === -print_group $func_zip - -echo %%=== "#" Miscellaneous functions === -print_group $func_helper - diff --git a/Utils/jsonlab/genlog.sh b/Utils/jsonlab/genlog.sh deleted file mode 100644 index aecb4182..00000000 --- a/Utils/jsonlab/genlog.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -git log --date=short --pretty='format: %cd [%h] %s' diff --git a/Utils/jsonlab/images/jsonlab-banner.png b/Utils/jsonlab/images/jsonlab-banner.png deleted file mode 100644 index 0ec83c0e4144fb89953304e70b7b62aca3ffdcb8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7350 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 c0R$fVKLPA*3KQ< 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 - - jsonlab - Qianqian Fang - fangqq@gmail.com - Northeastern University - Compact, portable, robust JSON/binary-JSON encoder/decoder for MATLAB/Octave - JSONLab is a free and open-source implementation of a JSON/UBJSON/MessagePack encoder and a decoder in the native MATLAB language. It can be used to convert a MATLAB data structure (array, struct, cell, struct array, cell array, and objects) into JSON/UBJSON/MessagePack formatted strings, or to decode a JSON/UBJSON/MessagePack file into MATLAB data structure. JSONLab supports both MATLAB and [http://www.gnu.org/software/octave GNU Octave] (a free MATLAB clone). - -JSON ([http://www.json.org/ JavaScript Object Notation]) is a highly portable, human-readable and [http://en.wikipedia.org/wiki/JSON "fat-free"] text format to represent complex and hierarchical data. It is as powerful as [http://en.wikipedia.org/wiki/XML XML] but less verbose. JSON format is widely used for data-exchange in applications. UBJSON ([http://ubjson.org/ Universal Binary JSON]) is a binary JSON format, specifically optimized for compact file size and better performance while keeping the semantics as simple as the text-based JSON format. Using the UBJSON format allows to wrap complex binary data in a flexible and extensible structure, making it possible to process complex and large dataset without accuracy loss due to text conversions. MessagePack is another binary JSON-like data format widely used in data exchange in web/native applications. It is slightly more compact than UBJSON, but is not directly readable compared to UBJSON. - -We envision that both JSON and its binary counterparts will play important roles as mainstream data-exchange formats for scientific research. -It has both the flexibility and generality as offered by other popular general-purpose file specifications, such as [http://www.hdfgroup.org/HDF5/whatishdf5.html HDF5] but with significantly reduced complexity and excellent readability. - -Towards this goal, we have developed the JData Specification (http://github.com/fangq/jdata) to standardize serializations of complex scientific data structures, such as N-D arrays, sparse/complex-valued arrays, trees, maps, tables and graphs using JSON/binary JSON constructs. The text and binary formatted JData files are syntactically compatible with JSON/UBJSON formats, and can be readily parsed using existing JSON and UBJSON parsers. - -Please note that data files produced by `saveubjson` may utilize a special "optimized header" to store N-D (N>1) arrays, as defined in the JData Specification Draft 2. This feature is not supported by UBJSON Specification Draft 12. To produce UBJSON files that can be parsed by UBJSON-Draft-12 compliant parsers, you must add the option `'NestArray',1 ` in the call to `saveubjson`. - ${PROJECT_ROOT}/images/jsonlab-logo.png - 2.9.8 - ${PROJECT_ROOT}/jsonlab.mltbx - - - - - d1e5cb15-4ada-479a-aafd-24b64efb8455 - *.m~ -.git* - true - - - - - - - - - false - - - - - - false - true - true - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ${PROJECT_ROOT} - - - ${PROJECT_ROOT}/AUTHORS.txt - ${PROJECT_ROOT}/ChangeLog.txt - ${PROJECT_ROOT}/Contents.m - ${PROJECT_ROOT}/DESCRIPTION - ${PROJECT_ROOT}/INDEX - ${PROJECT_ROOT}/LICENSE_BSD.txt - ${PROJECT_ROOT}/LICENSE_GPLv3.txt - ${PROJECT_ROOT}/README.rst - ${PROJECT_ROOT}/README.txt - ${PROJECT_ROOT}/base64decode.m - ${PROJECT_ROOT}/base64encode.m - ${PROJECT_ROOT}/decodevarname.m - ${PROJECT_ROOT}/encodevarname.m - ${PROJECT_ROOT}/examples - ${PROJECT_ROOT}/fast_match_bracket.m - ${PROJECT_ROOT}/filterjsonmmap.m - ${PROJECT_ROOT}/gendocs.sh - ${PROJECT_ROOT}/genlog.sh - ${PROJECT_ROOT}/getfromjsonpath.m - ${PROJECT_ROOT}/gzipdecode.m - ${PROJECT_ROOT}/gzipencode.m - ${PROJECT_ROOT}/images - ${PROJECT_ROOT}/isoctavemesh.m - ${PROJECT_ROOT}/jdatadecode.m - ${PROJECT_ROOT}/jdataencode.m - ${PROJECT_ROOT}/jload.m - ${PROJECT_ROOT}/jsave.m - ${PROJECT_ROOT}/jsonget.m - ${PROJECT_ROOT}/jsonopt.m - ${PROJECT_ROOT}/jsonset.m - ${PROJECT_ROOT}/loadbj.m - ${PROJECT_ROOT}/loadjd.m - ${PROJECT_ROOT}/loadjson.m - ${PROJECT_ROOT}/loadmsgpack.m - ${PROJECT_ROOT}/loadubjson.m - ${PROJECT_ROOT}/lz4decode.m - ${PROJECT_ROOT}/lz4encode.m - ${PROJECT_ROOT}/lz4hcdecode.m - ${PROJECT_ROOT}/lz4hcencode.m - ${PROJECT_ROOT}/lzipdecode.m - ${PROJECT_ROOT}/lzipencode.m - ${PROJECT_ROOT}/lzmadecode.m - ${PROJECT_ROOT}/lzmaencode.m - ${PROJECT_ROOT}/match_bracket.m - ${PROJECT_ROOT}/mergestruct.m - ${PROJECT_ROOT}/nestbracket2dim.m - ${PROJECT_ROOT}/package.json - ${PROJECT_ROOT}/savebj.m - ${PROJECT_ROOT}/savejd.m - ${PROJECT_ROOT}/savejson.m - ${PROJECT_ROOT}/savemsgpack.m - ${PROJECT_ROOT}/saveubjson.m - ${PROJECT_ROOT}/test - ${PROJECT_ROOT}/varargin2struct.m - ${PROJECT_ROOT}/zlibdecode.m - ${PROJECT_ROOT}/zlibencode.m - - - - - - jsonlab.mltbx - - - - /home/app/MATLAB/R2020a - - - - true - false - false - false - false - false - true - false - 5.6.14-050614-generic - false - true - glnxa64 - true - - - diff --git a/Utils/jsonlab/package.json b/Utils/jsonlab/package.json deleted file mode 100644 index cf976037..00000000 --- a/Utils/jsonlab/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "jsonlab", - "version": "2.9.8", - "description": "Compact, portable, robust JSON/binary-JSON encoder/decoder for MATLAB/Octave", - "directories": { - "example": "examples" - }, - "scripts": { - "test": "test/run_jsonlab_test.m" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/fangq/jsonlab.git" - }, - "author": "Qianqian Fang ", - "license": "GPL-3.0-or-later OR BSD-3-Clause", - "bugs": { - "url": "https://github.com/fangq/jsonlab/issues" - }, - "homepage": "https://neurojson.org/jsonlab/" -} diff --git a/Utils/jsonlab/test/run_jsonlab_test.m b/Utils/jsonlab/test/run_jsonlab_test.m deleted file mode 100644 index f5bcb1e9..00000000 --- a/Utils/jsonlab/test/run_jsonlab_test.m +++ /dev/null @@ -1,345 +0,0 @@ -function run_jsonlab_test(tests) -% -% run_jsonlab_test -% or -% run_jsonlab_test(tests) -% run_jsonlab_test({'js','jso','bj','bjo'}) -% -% Unit testing for JSONLab JSON, BJData/UBJSON encoders and decoders -% -% authors:Qianqian Fang (q.fang neu.edu) -% date: 2020/06/08 -% -% input: -% tests: is a cell array of strings, possible elements include -% 'js': test savejson/loadjson -% 'jso': test savejson/loadjson special options -% 'bj': test savebj/loadbj -% 'bjo': test savebj/loadbj special options -% -% license: -% BSD or GPL version 3, see LICENSE_{BSD,GPLv3}.txt files for details -% -% -- this function is part of JSONLab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) -% - -if(nargin==0) - tests={'js','jso','bj','bjo','jmap','bmap'}; -end - -%% -if(ismember('js',tests)) - fprintf(sprintf('%s\n',char(ones(1,79)*61))); - fprintf('Test JSON functions\n'); - fprintf(sprintf('%s\n',char(ones(1,79)*61))); - - test_jsonlab('single integer',@savejson,5,'[5]'); - test_jsonlab('single float',@savejson,3.14,'[3.14]'); - test_jsonlab('nan',@savejson,nan,'["_NaN_"]'); - test_jsonlab('inf',@savejson,inf,'["_Inf_"]'); - test_jsonlab('-inf',@savejson,-inf,'["-_Inf_"]'); - test_jsonlab('large integer',@savejson,uint64(2^64),'[18446744073709551616]'); - test_jsonlab('large negative integer',@savejson,int64(-2^63),'[-9223372036854775808]'); - test_jsonlab('boolean as 01',@savejson,[true,false],'[1,0]','compact',1); - test_jsonlab('empty array',@savejson,[],'[]'); - test_jsonlab('empty cell',@savejson,{},'[]'); - test_jsonlab('empty string',@savejson,'','""','compact',1); - test_jsonlab('string escape',@savejson,sprintf('jdata\n\b\ashall\tprevail\t"\"\\'),'"jdata\n\b\ashall\tprevail\t\"\"\\"'); - if(exist('string')) - test_jsonlab('string type',@savejson,string(sprintf('jdata\n\b\ashall\tprevail')),'["jdata\n\b\ashall\tprevail"]','compact',1); - test_jsonlab('string array',@savejson,[string('jdata'),string('shall'),string('prevail')],'["jdata","shall","prevail"]','compact',1); - end - test_jsonlab('empty name',@savejson,loadjson('{"":""}'),'{"":""}','compact',1); - if(exist('containers.Map')) - test_jsonlab('empty name with map',@savejson,loadjson('{"":""}','usemap',1),'{"":""}','compact',1); - end - test_jsonlab('row vector',@savejson,[1,2,3],'[1,2,3]'); - test_jsonlab('column vector',@savejson,[1;2;3],'[[1],[2],[3]]','compact',1); - test_jsonlab('mixed array',@savejson,{'a',1,0.9},'["a",1,0.9]','compact',1); - test_jsonlab('mixed array from string',@savejson,loadjson('["a",{"c":1}, [2,3]]'),'["a",{"c":1},[2,3]]','compact',1); - test_jsonlab('char array',@savejson,['AC';'EG'],'["AC","EG"]','compact',1); - test_jsonlab('maps',@savejson,struct('a',1,'b','test'),'{"a":1,"b":"test"}','compact',1); - test_jsonlab('2d array',@savejson,[1,2,3;4,5,6],'[[1,2,3],[4,5,6]]','compact',1); - test_jsonlab('non-uniform 2d array',@savejson,{[1,2],[3,4,5],[6,7]},'[[1,2],[3,4,5],[6,7]]','compact',1); - test_jsonlab('non-uniform array with length multiple of first element',@savejson,{[1,2],[3,4,5,6],[7,8]},'[[1,2],[3,4,5,6],[7,8]]','compact',1); - test_jsonlab('1d array with flexible white space',@savejson,loadjson(sprintf(' [ +1, \n -2e3 \n , 3.0E+00 ,\r+4e-0] ')),'[1,-2000,3,4]','compact',1); - test_jsonlab('2d array with flexible white space',@savejson,loadjson(sprintf(' [\r [\n 1 , \r\n 2\n, 3] ,\n[ 4, 5 , \t 6\t]\n] ')),'[[1,2,3],[4,5,6]]','compact',1); - test_jsonlab('3d (row-major) nested array',@savejson,reshape(1:(2*3*2),2,3,2),... - '[[[1,7],[3,9],[5,11]],[[2,8],[4,10],[6,12]]]','compact',1,'nestarray',1); - test_jsonlab('3d (column-major) nested array',@savejson,reshape(1:(2*3*2),2,3,2),... - '[[[1,2],[3,4],[5,6]],[[7,8],[9,10],[11,12]]]','compact',1,'nestarray',1,'formatversion',1.9); - test_jsonlab('3d annotated array',@savejson,reshape(int8(1:(2*3*2)),2,3,2),... - '{"_ArrayType_":"int8","_ArraySize_":[2,3,2],"_ArrayData_":[1,7,3,9,5,11,2,8,4,10,6,12]}','compact',1); - test_jsonlab('complex number',@savejson,single(2+4i),... - '{"_ArrayType_":"single","_ArraySize_":[1,1],"_ArrayIsComplex_":true,"_ArrayData_":[[2],[4]]}','compact',1); - test_jsonlab('empty sparse matrix',@savejson,sparse(2,3),... - '{"_ArrayType_":"double","_ArraySize_":[2,3],"_ArrayIsSparse_":true,"_ArrayData_":[]}','compact',1); - test_jsonlab('real sparse matrix',@savejson,sparse([0,3,0,1,4]'),... - '{"_ArrayType_":"double","_ArraySize_":[5,1],"_ArrayIsSparse_":true,"_ArrayData_":[[2,4,5],[3,1,4]]}','compact',1); - test_jsonlab('complex sparse matrix',@savejson,sparse([0,3i,0,1,4i].'),... - '{"_ArrayType_":"double","_ArraySize_":[5,1],"_ArrayIsComplex_":true,"_ArrayIsSparse_":true,"_ArrayData_":[[2,4,5],[0,1,0],[3,0,4]]}','compact',1); - test_jsonlab('heterogeneous cell',@savejson,{{1,{2,3}},{4,5},{6};{7},{8,9},{10}},... - '[[[1,[2,3]],[4,5],[6]],[[7],[8,9],[10]]]','compact',1); - test_jsonlab('struct array',@savejson,repmat(struct('i',1.1,'d','str'),[1,2]),... - '[{"i":1.1,"d":"str"},{"i":1.1,"d":"str"}]','compact',1); - test_jsonlab('encoded fieldnames',@savejson,struct(encodevarname('_i'),1,encodevarname('i_'),'str'),... - '{"_i":1,"i_":"str"}','compact',1); - if(exist('OCTAVE_VERSION','builtin')~=0) - test_jsonlab('encoded fieldnames without decoding',@savejson,struct(encodevarname('_i'),1,encodevarname('i_'),'str'),... - '{"_i":1,"i_":"str"}','compact',1,'UnpackHex',0); - else - test_jsonlab('encoded fieldnames without decoding',@savejson,struct(encodevarname('_i'),1,encodevarname('i_'),'str'),... - '{"x0x5F_i":1,"i_":"str"}','compact',1,'UnpackHex',0); - end - if(exist('containers.Map')) - test_jsonlab('containers.Map',@savejson,containers.Map({'Andy','^_^'},{true,'-_-'}),... - '{"Andy":true,"^_^":"-_-"}','compact',1,'usemap',1); - end - if(exist('istable')) - test_jsonlab('simple table',@savejson,table({'Andy','^_^'},{true,'-_-'}),... - '{"_TableCols_":["Var1","Var2"],"_TableRows_":[],"_TableRecords_":[["Andy","^_^"],[true,"-_-"]]}','compact',1); - end - if(exist('bandwidth')) - lband=2; - uband=1; - a=double(full(spdiags(true(4,lband+uband+1),-uband:lband,3,4))); - a(a~=0)=find(a); - - test_jsonlab('lower band matrix',@savejson,tril(a),... - '{"_ArrayType_":"double","_ArraySize_":[3,4],"_ArrayZipSize_":[2,3],"_ArrayShape_":["lowerband",1],"_ArrayData_":[1,5,9,0,2,6]}','compact',1,'usearrayshape',1); - test_jsonlab('upper band matrix',@savejson,triu(a),... - '{"_ArrayType_":"double","_ArraySize_":[3,4],"_ArrayZipSize_":[3,3],"_ArrayShape_":["upperband",2],"_ArrayData_":[7,11,0,4,8,12,1,5,9]}','compact',1,'usearrayshape',1); - test_jsonlab('diag matrix',@savejson,tril(triu(a)),... - '{"_ArrayType_":"double","_ArraySize_":[3,4],"_ArrayShape_":"diag","_ArrayData_":[1,5,9]}','compact',1,'usearrayshape',1); - test_jsonlab('band matrix',@savejson,a,... - '{"_ArrayType_":"double","_ArraySize_":[3,4],"_ArrayZipSize_":[4,3],"_ArrayShape_":["band",2,1],"_ArrayData_":[7,11,0,4,8,12,1,5,9,0,2,6]}','compact',1,'usearrayshape',1); - a=a(:,1:3); - a=uint8(tril(a)+tril(a)'); - test_jsonlab('symmetric band matrix',@savejson,a,... - '{"_ArrayType_":"uint8","_ArraySize_":[3,3],"_ArrayZipSize_":[2,3],"_ArrayShape_":["lowersymmband",1],"_ArrayData_":[2,10,18,0,2,6]}','compact',1,'usearrayshape',1); - a(a==0)=1; - test_jsonlab('lower triangular matrix',@savejson,tril(a),... - '{"_ArrayType_":"uint8","_ArraySize_":[3,3],"_ArrayShape_":"lower","_ArrayData_":[2,2,10,1,6,18]}','compact',1,'usearrayshape',1); - test_jsonlab('upper triangular matrix',@savejson,triu(a),... - '{"_ArrayType_":"uint8","_ArraySize_":[3,3],"_ArrayShape_":"upper","_ArrayData_":[2,2,1,10,6,18]}','compact',1,'usearrayshape',1); - end - try - val=zlibencode('test'); - a=uint8(eye(5)); - a(20,1)=1; - test_jsonlab('zlib/zip compression (level 6)',@savejson,a,... - sprintf('{"_ArrayType_":"uint8","_ArraySize_":[20,5],"_ArrayZipSize_":[1,100],"_ArrayZipType_":"zlib","_ArrayZipData_":"eJxjZAABRhwkxQBsDAACIQAH\n"}'),... - 'compact',1, 'Compression','zlib','CompressArraySize',0) % nestarray for 4-D or above is not working - test_jsonlab('gzip compression (level 6)',@savejson,a,... - sprintf('{"_ArrayType_":"uint8","_ArraySize_":[20,5],"_ArrayZipSize_":[1,100],"_ArrayZipType_":"gzip","_ArrayZipData_":"H4sIAAAAAAAAA2NkAAFGHCTFAGwMAF9Xq6VkAAAA\n"}'),... - 'compact',1, 'Compression','gzip','CompressArraySize',0) % nestarray for 4-D or above is not working - test_jsonlab('lzma compression (level 5)',@savejson,a,... - sprintf('{"_ArrayType_":"uint8","_ArraySize_":[20,5],"_ArrayZipSize_":[1,100],"_ArrayZipType_":"lzma","_ArrayZipData_":"XQAAEABkAAAAAAAAAAAAgD1IirvlZSEY7DH///taoAA=\n"}'),... - 'compact',1, 'Compression','lzma','CompressArraySize',0) % nestarray for 4-D or above is not working - catch - end -end -%% -if(ismember('jso',tests)) - fprintf(sprintf('%s\n',char(ones(1,79)*61))); - fprintf('Test JSON function options\n'); - fprintf(sprintf('%s\n',char(ones(1,79)*61))); - - test_jsonlab('boolean',@savejson,[true,false],'[true,false]','compact',1,'ParseLogical',1); - test_jsonlab('nan option',@savejson,nan,'["_nan_"]','NaN','"_nan_"'); - test_jsonlab('inf option',@savejson,-inf,'["-inf"]','Inf','"$1inf"'); - test_jsonlab('output int format',@savejson,uint8(5),'[ 5]','IntFormat','%3d'); - test_jsonlab('output float format',@savejson,pi,'[3.142]','FloatFormat','%5.3f'); - test_jsonlab('remove singlet array',@savejson,{struct('a',1),5},'[{"a":1},5]','compact',1,'SingletArray',0); - test_jsonlab('keep singlet array',@savejson,{struct('a',1),5},'[[{"a":[1]}],[5]]','compact',1,'SingletArray',1); - test_jsonlab('test no datalink',@savejson,loadjson(savejson('a',struct(encodevarname('_DataLink_'),... - '../examples/example2.json:$.glossary.title'))),'{"a":[{"_DataLink_":"../examples/example2.json:$.glossary.title"}]}','compact',1,'SingletArray',1); - test_jsonlab('test maxlinklevel',@savejson,loadjson(savejson('a',struct(encodevarname('_DataLink_'),... - '../examples/example2.json:$.glossary.title')),'maxlinklevel',1),'{"a":"example glossary"}','compact',1,'SingletArray',1); -end - - -%% -if(ismember('bj',tests)) - fprintf(sprintf('%s\n',char(ones(1,79)*61))); - fprintf('Test Binary JSON functions\n'); - fprintf(sprintf('%s\n',char(ones(1,79)*61))); - - test_jsonlab('uint8 integer',@savebj,2^8-1,'U<255>','debug',1); - test_jsonlab('uint16 integer',@savebj,2^8,'u<256>','debug',1); - test_jsonlab('int8 integer',@savebj,-2^7,'i<-128>','debug',1); - test_jsonlab('int16 integer',@savebj,-2^7-1,'I<-129>','debug',1); - test_jsonlab('int32 integer',@savebj,-2^15-1,'l<-32769>','debug',1); - test_jsonlab('uint16 integer',@savebj,2^16-1,'u<65535>','debug',1); - test_jsonlab('uint32 integer',@savebj,2^16,'m<65536>','debug',1); - test_jsonlab('uint32 integer',@savebj,2^32-1,'m<4294967295>','debug',1); - test_jsonlab('int32 integer',@savebj,-2^31,'l<-2147483648>','debug',1); - test_jsonlab('single float',@savebj,3.14,'D<3.14>','debug',1); - test_jsonlab('nan',@savebj,nan,'D','debug',1); - test_jsonlab('inf',@savebj,inf,'D','debug',1); - test_jsonlab('-inf',@savebj,-inf,'D<-Inf>','debug',1); - test_jsonlab('uint64 integer',@savebj,uint64(2^64),'M<18446744073709551616>','debug',1); - test_jsonlab('int64 negative integer',@savebj,int64(-2^63),'L<-9223372036854775808>','debug',1); - test_jsonlab('boolean as 01',@savebj,[true,false],'[U<1>U<0>]','debug',1,'nestarray',1); - test_jsonlab('empty array',@savebj,[],'Z','debug',1); - test_jsonlab('empty cell',@savebj,{},'Z','debug',1); - test_jsonlab('empty string',@savebj,'','SU<0>','debug',1); - test_jsonlab('skip no-op before marker and after value',@savebj,loadbj(char(['NN[NU' char(5) 'NNNU' char(1) ']'])),'[$U#U<2><5><1>','debug',1); - test_jsonlab('string escape',@savebj,sprintf('jdata\n\b\ashall\tprevail\t"\"\\'),sprintf('SU<25>jdata\n\b\ashall\tprevail\t\"\"\\'),'debug',1); - if(exist('string')) - test_jsonlab('string type',@savebj,string(sprintf('jdata\n\b\ashall\tprevail')),sprintf('[SU<21>jdata\n\b\ashall\tprevail]'),'debug',1); - test_jsonlab('string array',@savebj,[string('jdata');string('shall');string('prevail')],'[[SU<5>jdataSU<5>shallSU<7>prevail]]','debug',1); - end - test_jsonlab('empty name',@savebj,loadbj(['{U' 0 'U' 2 '}']),'{U<0>U<2>}','debug',1); - if(exist('containers.Map')) - test_jsonlab('empty name with map',@savebj,loadbj(['{U' 0 'U' 2 '}'],'usemap',1),'{U<0>U<2>}','debug',1); - end - test_jsonlab('row vector',@savebj,[1,2,3],'[$U#U<3><1><2><3>','debug',1); - test_jsonlab('column vector',@savebj,[1;2;3],'[$U#[$U#U<2><3><1><1><2><3>','debug',1); - test_jsonlab('mixed array',@savebj,{'a',1,0.9},'[CaU<1>D<0.9>]','debug',1); - test_jsonlab('char array',@savebj,['AC';'EG'],'[SU<2>ACSU<2>EG]','debug',1); - test_jsonlab('maps',@savebj,struct('a',1,'b','test'),'{U<1>aU<1>U<1>bSU<4>test}','debug',1); - test_jsonlab('2d array',@savebj,[1,2,3;4,5,6],'[$U#[$U#U<2><2><3><1><2><3><4><5><6>','debug',1); - test_jsonlab('3d (row-major) nested array',@savebj,reshape(1:(2*3*2),2,3,2),... - '[[[U<1>U<7>][U<3>U<9>][U<5>U<11>]][[U<2>U<8>][U<4>U<10>][U<6>U<12>]]]','debug',1,'nestarray',1); - test_jsonlab('3d (column-major) nested array',@savebj,reshape(1:(2*3*2),2,3,2),... - '[[[U<1>U<2>][U<3>U<4>][U<5>U<6>]][[U<7>U<8>][U<9>U<10>][U<11>U<12>]]]','debug',1,'nestarray',1,'formatversion',1.9); - test_jsonlab('3d annotated array',@savebj,reshape(int8(1:(2*3*2)),2,3,2),... - '{U<11>_ArrayType_SU<4>int8U<11>_ArraySize_[$U#U<3><2><3><2>U<11>_ArrayData_[$U#U<12><1><7><3><9><5><11><2><8><4><10><6><12>}','debug',1); - test_jsonlab('complex number',@savebj,single(2+4i),... - '{U<11>_ArrayType_SU<6>singleU<11>_ArraySize_[$U#U<2><1><1>U<16>_ArrayIsComplex_TU<11>_ArrayData_[$U#[$U#U<2><2><1><2><4>}','debug',1); - test_jsonlab('empty sparse matrix',@savebj,sparse(2,3),... - '{U<11>_ArrayType_SU<6>doubleU<11>_ArraySize_[$U#U<2><2><3>U<15>_ArrayIsSparse_TU<11>_ArrayData_Z}','debug',1); - test_jsonlab('real sparse matrix',@savebj,sparse([0,3,0,1,4]'),... - '{U<11>_ArrayType_SU<6>doubleU<11>_ArraySize_[$U#U<2><5><1>U<15>_ArrayIsSparse_TU<11>_ArrayData_[$U#[$U#U<2><2><3><2><4><5><3><1><4>}','debug',1); - test_jsonlab('complex sparse matrix',@savebj,sparse([0,3i,0,1,4i].'),... - '{U<11>_ArrayType_SU<6>doubleU<11>_ArraySize_[$U#U<2><5><1>U<16>_ArrayIsComplex_TU<15>_ArrayIsSparse_TU<11>_ArrayData_[$U#[$U#U<2><3><3><2><4><5><0><1><0><3><0><4>}','debug',1); - test_jsonlab('heterogeneous cell',@savebj,{{1,{2,3}},{4,5},{6};{7},{8,9},{10}},... - '[[[U<1>[U<2>U<3>]][U<4>U<5>][U<6>]][[U<7>][U<8>U<9>][U<10>]]]','debug',1); - test_jsonlab('struct array',@savebj,repmat(struct('i',1.1,'d','str'),[1,2]),... - '[{U<1>iD<1.1>U<1>dSU<3>str}{U<1>iD<1.1>U<1>dSU<3>str}]','debug',1); - test_jsonlab('encoded fieldnames',@savebj,struct(encodevarname('_i'),1,encodevarname('i_'),'str'),... - '{U<2>_iU<1>U<2>i_SU<3>str}','debug',1); - if(exist('OCTAVE_VERSION','builtin')~=0) - test_jsonlab('encoded fieldnames without decoding',@savebj,struct(encodevarname('_i'),1,encodevarname('i_'),'str'),... - '{U<2>_iU<1>U<2>i_SU<3>str}','debug',1,'UnpackHex',0); - else - test_jsonlab('encoded fieldnames without decoding',@savebj,struct(encodevarname('_i'),1,encodevarname('i_'),'str'),... - '{U<7>x0x5F_iU<1>U<2>i_SU<3>str}','debug',1,'UnpackHex',0); - end - if(exist('containers.Map')) - test_jsonlab('containers.Map',@savebj,containers.Map({'Andy','^_^'},{true,'-_-'}),... - '{U<4>AndyTU<3>^_^SU<3>-_-}','debug',1,'usemap',1); - end - if(exist('istable')) - test_jsonlab('simple table',@savebj,table({'Andy','^_^'},{true,'-_-'}),... - '{U<11>_TableCols_[SU<4>Var1SU<4>Var2]U<11>_TableRows_ZU<14>_TableRecords_[[SU<4>AndySU<3>^_^][TSU<3>-_-]]}','debug',1); - end - if(exist('bandwidth')) - lband=2; - uband=1; - a=double(full(spdiags(true(4,lband+uband+1),-uband:lband,3,4))); - a(a~=0)=find(a); - - test_jsonlab('lower band matrix',@savebj,tril(a),... - '{U<11>_ArrayType_SU<6>doubleU<11>_ArraySize_[$U#U<2><3><4>U<14>_ArrayZipSize_[$U#U<2><2><3>U<12>_ArrayShape_[SU<9>lowerbandU<1>]U<11>_ArrayData_[$U#U<6><1><5><9><0><2><6>}','debug',1,'usearrayshape',1); - test_jsonlab('upper band matrix',@savebj,triu(a),... - '{U<11>_ArrayType_SU<6>doubleU<11>_ArraySize_[$U#U<2><3><4>U<14>_ArrayZipSize_[$U#U<2><3><3>U<12>_ArrayShape_[SU<9>upperbandU<2>]U<11>_ArrayData_[$U#U<9><7><11><0><4><8><12><1><5><9>}','debug',1,'usearrayshape',1); - test_jsonlab('diag matrix',@savebj,tril(triu(a)),... - '{U<11>_ArrayType_SU<6>doubleU<11>_ArraySize_[$U#U<2><3><4>U<12>_ArrayShape_SU<4>diagU<11>_ArrayData_[$U#U<3><1><5><9>}','debug',1,'usearrayshape',1); - test_jsonlab('band matrix',@savebj,a,... - '{U<11>_ArrayType_SU<6>doubleU<11>_ArraySize_[$U#U<2><3><4>U<14>_ArrayZipSize_[$U#U<2><4><3>U<12>_ArrayShape_[SU<4>bandU<2>U<1>]U<11>_ArrayData_[$U#U<12><7><11><0><4><8><12><1><5><9><0><2><6>}','debug',1,'usearrayshape',1); - a=a(:,1:3); - a=uint8(tril(a)+tril(a)'); - test_jsonlab('symmetric band matrix',@savebj,a,... - '{U<11>_ArrayType_SU<5>uint8U<11>_ArraySize_[$U#U<2><3><3>U<14>_ArrayZipSize_[$U#U<2><2><3>U<12>_ArrayShape_[SU<13>lowersymmbandU<1>]U<11>_ArrayData_[$U#U<6><2><10><18><0><2><6>}','debug',1,'usearrayshape',1); - a(a==0)=1; - test_jsonlab('lower triangular matrix',@savebj,tril(a),... - '{U<11>_ArrayType_SU<5>uint8U<11>_ArraySize_[$U#U<2><3><3>U<12>_ArrayShape_SU<5>lowerU<11>_ArrayData_[$U#U<6><2><2><10><1><6><18>}','debug',1,'usearrayshape',1); - test_jsonlab('upper triangular matrix',@savebj,triu(a),... - '{U<11>_ArrayType_SU<5>uint8U<11>_ArraySize_[$U#U<2><3><3>U<12>_ArrayShape_SU<5>upperU<11>_ArrayData_[$U#U<6><2><2><1><10><6><18>}','debug',1,'usearrayshape',1); - end - try - val=zlibencode('test'); - a=uint8(eye(5)); - a(20,1)=1; - test_jsonlab('zlib/zip compression (level 6)',@savebj,a,... - '{U<11>_ArrayType_SU<5>uint8U<11>_ArraySize_[$U#U<2><20><5>U<14>_ArrayZipSize_[$U#U<2><1><100>U<14>_ArrayZipType_SU<4>zlibU<14>_ArrayZipData_[$U#U<18><120><156><99><100><0><1><70><28><36><197><0><108><12><0><2><33><0><7>}',... - 'debug',1, 'Compression','zlib','CompressArraySize',0) % nestarray for 4-D or above is not working - test_jsonlab('gzip compression (level 6)',@savebj,a,... - '{U<11>_ArrayType_SU<5>uint8U<11>_ArraySize_[$U#U<2><20><5>U<14>_ArrayZipSize_[$U#U<2><1><100>U<14>_ArrayZipType_SU<4>gzipU<14>_ArrayZipData_[$U#U<30><31><139><8><0><0><0><0><0><0><3><99><100><0><1><70><28><36><197><0><108><12><0><95><87><171><165><100><0><0><0>}',... - 'debug',1, 'Compression','gzip','CompressArraySize',0) % nestarray for 4-D or above is not working - test_jsonlab('lzma compression (level 5)',@savebj,a,... - '{U<11>_ArrayType_SU<5>uint8U<11>_ArraySize_[$U#U<2><20><5>U<14>_ArrayZipSize_[$U#U<2><1><100>U<14>_ArrayZipType_SU<4>lzmaU<14>_ArrayZipData_[$U#U<32><93><0><0><16><0><100><0><0><0><0><0><0><0><0><0><128><61><72><138><187><229><101><33><24><236><49><255><255><251><90><160><0>}',... - 'debug',1, 'Compression','lzma','CompressArraySize',0) % nestarray for 4-D or above is not working - catch - end -end - -%% -if(ismember('bjo',tests)) - fprintf(sprintf('%s\n',char(ones(1,79)*61))); - fprintf('Test Binary JSON function options\n'); - fprintf(sprintf('%s\n',char(ones(1,79)*61))); - - test_jsonlab('remove ubjson optimized array header',@savebj,[1,2,3],'[U<1>U<2>U<3>]','debug',1,'nestarray',1); - test_jsonlab('limit to ubjson signed integer',@savebj,256,'I<256>','debug',1,'ubjson',1); - test_jsonlab('limit to ubjson integer markers',@savebj,2^32-1,'L<4294967295>','debug',1,'ubjson',1); - test_jsonlab('H marker for out of bound integer',@savebj,2^64-1,'HU<20>18446744073709551616','debug',1,'ubjson',1); - test_jsonlab('do not downcast integers to the shortest format',@savebj,int32(5),'l<5>','debug',1,'keeptype',1); - test_jsonlab('do not downcast integer array to the shortest format',@savebj,int32([5,6]),'[$l#U<2><5><6>','debug',1,'keeptype',1); - test_jsonlab('test little endian uint32',@savebj,typecast(uint8('abcd'),'uint32'),'mabcd','endian','L'); - test_jsonlab('test big endian uint32',@savebj,typecast(uint8('abcd'),'uint32'),'mdcba','endian','B'); - test_jsonlab('test little endian double',@savebj,typecast(uint8('01234567'),'double'),'D01234567','endian','L'); - test_jsonlab('test big endian double',@savebj,typecast(uint8('01234567'),'double'),'D76543210','endian','B'); - test_jsonlab('test default int endian for savebj',@savebj,typecast(uint8('jd'),'uint16'),'ujd'); - test_jsonlab('test default int endian for saveubjson',@saveubjson,typecast(uint8('jd'),'uint16'),'Idj'); - test_jsonlab('test default float endian for savebj',@savebj,typecast(uint8('1e05'),'single'),'d1e05'); - test_jsonlab('test default float endian for saveubjson',@saveubjson,typecast(uint8('12345678'),'double'),'D87654321'); -end - -%% -if(ismember('jmap',tests)) - fprintf(sprintf('%s\n',char(ones(1,79)*61))); - fprintf('Test JSON mmap\n'); - fprintf(sprintf('%s\n',char(ones(1,79)*61))); - - test_jsonlab('mmap of a 1D numerical array',@savejson,loadjson('[1,2,3]','mmaponly',1),'[["$",[1,7]]]','compact',1); - test_jsonlab('mmap of a 1D mixed array',@savejson,loadjson('[1,"2",3]','mmaponly',1),'[["$",[1,9]]]','compact',1); - test_jsonlab('mmap of a 2D array',@savejson,loadjson('[[1,2,3],[4,5,6]]','mmaponly',1),'[["$",[1,17]]]','compact',1); - test_jsonlab('mmap of concatenated json',@savejson,loadjson('[1,2,3][4,5,6]','mmaponly',1),'[["$",[1,7]],["$1",[8,7]]]','compact',1); - test_jsonlab('mmap of concatenated json objects',@savejson,loadjson('[1,2,3]{"a":[4,5]}','mmaponly',1),'[["$",[1,7]],["$1",[8,11]],["$1.a",[13,5]]]','compact',1); - test_jsonlab('mmap of an array with an object',@savejson,loadjson('[1,2,{"a":3}]','mmaponly',1),... - '[["$",[1,13]],["$[0]",[2,1]],["$[1]",[4,1]],["$[2]",[6,7]],["$[2].a",[11,1]]]','compact',1); - test_jsonlab('mmap of an object',@savejson,loadjson('{"a":1,"b":[2,3]}','mmaponly',1),... - '[["$",[1,17]],["$.a",[6,1]],["$.b",[12,5]]]','compact',1); - test_jsonlab('mmap of object with white-space',@savejson,loadjson('{"a":1 , "b" : [2,3]}','mmaponly',1),... - '[["$",[1,23]],["$.a",[6,1]],["$.b",[18,5,2]]]','compact',1); - test_jsonlab('mmapinclude option',@savejson,loadjson('[[1,2,3],{"a":[4,5]}]','mmaponly',1,'mmapinclude','.a'),... - '[["$[1].a",[15,5]]]','compact',1); - test_jsonlab('mmapexclude option',@savejson,loadjson('[[1,2,3],{"a":[4,5]}]','mmaponly',1,'mmapexclude',{'[0]','[1]','[2]'}),... - '[["$",[1,21]]]','compact',1); - test_jsonlab('json with indentation',@savejson,loadjson(savejson({[1,2,3],struct('a',[4,5])}),'mmaponly',1,'mmapinclude','.a'),... - '[["$[1].a",[22,7]]]','compact',1); -end - -%% -if(ismember('bmap',tests)) - fprintf(sprintf('%s\n',char(ones(1,79)*61))); - fprintf('Test Binary JSON mmap\n'); - fprintf(sprintf('%s\n',char(ones(1,79)*61))); - - test_jsonlab('mmap of a 1D numerical array',@savejson,loadbj(savebj([1,2,3]),'mmaponly',1),'[["$",[1,9]]]','compact',1); - test_jsonlab('mmap of a 1D mixed array',@savejson,loadbj(savebj({1,'2',3}),'mmaponly',1),'[["$",[1,8]],["$[0]",[2,2]],["$[1]",[4,2]],["$[2]",[6,2]]]','compact',1); - test_jsonlab('mmap of a 2D array',@savejson,loadbj(savebj([[1,2,3],[4,5,6]]),'mmaponly',1),'[["$",[1,12]]]','compact',1); - test_jsonlab('mmap of an array with an object',@savejson,loadbj(savebj({1,2,struct('a',3)}),'mmaponly',1),... - '[["$",[1,13]],["$[0]",[2,2]],["$[1]",[4,2]],["$[2]",[6,7]],["$[2].a",[10,2]]]','compact',1); - test_jsonlab('mmap of an object',@savejson,loadbj(savebj(struct('a',1,'b',[2,3])),'mmaponly',1),... - '[["$",[1,18]],["$.a",[5,2]],["$.b",[10,8]]]','compact',1); - test_jsonlab('mmapinclude option',@savejson,loadbj(savebj({[1,2,3],struct('a',[4,5])}),'mmaponly',1,'mmapinclude','.a'),... - '[["$[1].a",[15,8]]]','compact',1); - test_jsonlab('mmapexclude option',@savejson,loadbj(savebj({[1,2,3],struct('a',[4,5])}),'mmaponly',1,'mmapexclude',{'[0]','[1]','[2]'}),... - '[["$",[1,24]]]','compact',1); - test_jsonlab('test multiple root objects with N padding',@savejson,loadbj([savebj({[1,2,3],struct('a',[4,5])}) 'NNN' savebj(struct('b',[4,5]))],'mmaponly',1,'mmapinclude','.b'),... - '[["$1.b",[32,8]]]','compact',1); -end \ No newline at end of file diff --git a/Utils/jsonlab/test/test_jsonlab.m b/Utils/jsonlab/test/test_jsonlab.m deleted file mode 100644 index e315f71d..00000000 --- a/Utils/jsonlab/test/test_jsonlab.m +++ /dev/null @@ -1,32 +0,0 @@ -function test_jsonlab(testname,fhandle,input,expected,varargin) -res=fhandle('',input,varargin{:}); -if(~isequal(strtrim(res),expected)) - warning('Test %s: failed: expected ''%s'', obtained ''%s''',testname,expected,res); -else - fprintf(1,'Testing %s: ok\n\toutput:''%s''\n',testname,strtrim(res)); - if(regexp(res,'^[\[\{A-Za-z]')) - handleinfo=functions(fhandle); - loadfunname=regexprep(handleinfo.function,'^save','load'); - loadfun=str2func(loadfunname); - if(strcmp(loadfunname,'loadbj')) - newres=loadfun(fhandle('',input,varargin{:},'debug',0),varargin{:}); - else - newres=loadfun(res,varargin{:}); - end - if(exist('isequaln')) - try - if(isequaln(newres,input)) - fprintf(1,'\t%s successfully restored the input\n',loadfunname); - end - catch - end - else - try - if(newres==input) - fprintf(1,'\t%s successfully restored the input\n',loadfunname); - end - catch - end - end - end -end \ No newline at end of file diff --git a/Utils/submodules/cleanupSharedLibs.m b/Utils/submodules/cleanupSharedLibs.m deleted file mode 100644 index af537e20..00000000 --- a/Utils/submodules/cleanupSharedLibs.m +++ /dev/null @@ -1,17 +0,0 @@ -function cleanupSharedLibs() -submodules = parseGitSubmodulesFile(); -for ii = 1:size(submodules,1) - submodulepath = submodules{ii,3}; - [~, submodulename] = fileparts(submodules{ii,1}); - if ispathvalid_startup(submodulepath) - if ispathvalid_startup([submodulename, '.old/']) - fprintf('Deleteing folder %s\n', [submodulename, '.old/']); - rmdir([submodulename, '.old/'],'s') - end - fprintf('Moving %s to %s\n', submodulepath, [submodulename, '.old']); - copyFolderContents(submodulepath, [submodulename, '.old/']); - fprintf('Removing contents of %s\n', submodulepath); - removeFolderContents(submodulepath); - end - fprintf('\n'); -end diff --git a/Utils/submodules/downloadSharedLibs.m b/Utils/submodules/downloadSharedLibs.m deleted file mode 100644 index bb5a086e..00000000 --- a/Utils/submodules/downloadSharedLibs.m +++ /dev/null @@ -1,175 +0,0 @@ -function [cmds, errs, msgs] = downloadSharedLibs(options, appname) -cmds = {}; -errs = 0; -msgs = {}; - -if ~exist('options','var') || (isnumeric(options) && options==0) - options = 'init'; -end - -s = parseGitSubmodulesFile(); -if ~optionExists_startup(options,'init') && ~optionExists_startup(options,'update') - addSearchPaths(s); - return; -end - -% Check for missing libs -kk = checkMissingLibraries(s); -if isempty(kk) && optionExists_startup(options, 'init') - return; -end - -if optionExists_startup(options, 'update') - [cmds, errs, msgs] = gitSubmodulesUpdate(pwd, options); -else - [cmds, errs, msgs] = gitSubmodulesInit(pwd, options); -end - -% Check again for missing libs -kk = checkMissingLibraries(s); -if isempty(kk) && (optionExists_startup(options, 'init') || all(errs==0)) - return; -end - -% Try to install missing libs without git -branch = warningGitFailedToInstall(s(kk,:), appname); -if ~isempty(branch) - if optionExists_startup(options, 'init') - downloadSubmodulesWithoutGit(s(kk,:), branch); - elseif optionExists_startup(options, 'update') - updateSubmodulesWithoutGit(s, branch); - end -end - -% Check again for missing libs -kk = checkMissingLibraries(s); -if isempty(kk) - errs = 0; - return; -end - -q = warningManualInstallRequired(s(kk,:)); -if q==1 - errs = -2; -else - paths = searchFiles(); - if isempty(paths) - errs = -1; - end -end - - - -% ---------------------------------------------------------- -function kk = checkMissingLibraries(s) -kk = []; -for ii = 1:size(s,1) - if isIncompleteSubmodule(s{ii,3}) - removeFolderContents(s{ii,3}); - kk = [kk, ii]; %#ok - else - addSearchPaths(s(ii,:)); - end -end - - -% ---------------------------------------------------------- -function branch = warningGitFailedToInstall(s, appname) -ii = 1; -branch = guessBranch(s, appname); -msg{ii} = sprintf('Git was not able to install the following libraries required by this application:\n\n'); ii = ii+1; -for jj = 1:size(s,1) - msg{ii} = sprintf(' %s\n', s{jj,1}); ii = ii+1; -end -msg{ii} = sprintf('\n'); ii = ii+1; -msg{ii} = sprintf('Git might not be installed on your computer. \n'); ii = ii+1; -msg{ii} = sprintf('These libraries can still be installed without git. The assumed submodule branch that matches \n'); ii = ii+1; -msg{ii} = sprintf('the branch of the parent repo, ''%s'', is ''%s''\n\n', appname, branch); ii = ii+1; %#ok -msg = [msg{:}]; - -fprintf(msg) -pause(2); - - - -% ---------------------------------------------------------- -function q = warningManualInstallRequired(s) -ii = 1; -msg{ii} = sprintf('WARNING: The following libraries required by this application are still missing:\n\n'); ii = ii+1; -for jj = 1:size(s,1) - msg{ii} = sprintf(' %s\n', s{jj,1}); ii = ii+1; -end -msg{ii} = sprintf('\n'); ii = ii+1; %#ok<*SPRINTFN> -msg{ii} = sprintf('Either a) install git and rerun setpaths or b) download the submodules manually and provide their locations. '); ii = ii+1; -msg{ii} = sprintf('Select option:'); -msg = [msg{:}]; - -q = menu(msg, {'Quit setpaths, install git and rerun setpaths','Download submodules manually and provide their locations'}); - - - - -% ---------------------------------------------------------- -function addSearchPaths(s) -exclSearchList = {'.git'}; -for ii = 1:size(s,1) - foo = findDotMFolders(s{ii,3}, exclSearchList); - for kk = 1:length(foo) - addpath(foo{kk}, '-end'); - setpermissions(foo{kk}); - end -end - - - - -% --------------------------------------------------- -function setpermissions(appPath) -if isunix() || ismac() - if ~isempty(strfind(appPath, '/bin')) %#ok - fprintf(sprintf('chmod 755 %s/*\n', appPath)); - files = dir([appPath, '/*']); - if ~isempty(files) - system(sprintf('chmod 755 %s/*', appPath)); - end - end -end - - - - -% ---------------------------------------------------- -function branchGuess = guessBranch(submodules, appname) -branchGuess = ''; -rootdir = fileparts(which([appname, '.m'])); -k = strfind(appname, 'GUI'); -if ~isempty(k) - appname0 = appname(1:k-1); -else - appname0 = appname; -end -k = strfind(rootdir, appname0); -if (k+length(appname0)) <= length(rootdir) - j = 0; - if rootdir(k+length(appname0))=='-' - j = 1; - end - branchGuess = rootdir(k+length(appname0)+j:end); -end -fprintf('\nBranch guess: ''%s''\n', branchGuess); - -% Check to see if submodule urls exist. If not edfault to 'master' branches. -for ii = 1:size(submodules,1) - url = submodules{ii,1}; - - urlfull = sprintf('%s/archive/refs/heads/%s.zip', url, branchGuess); - [~, urlExists] = urlread(urlfull); %#ok<*URLRD> - if urlExists - fprintf('Success: %s exists\n', urlfull); - else - fprintf('Failed: %s does NOT exist. Switching to ''master'' branch\n', urlfull); - branchGuess = 'master'; - end -end -fprintf('\n'); - diff --git a/Utils/submodules/downloadSubmodulesWithoutGit.m b/Utils/submodules/downloadSubmodulesWithoutGit.m deleted file mode 100644 index f9f1bda2..00000000 --- a/Utils/submodules/downloadSubmodulesWithoutGit.m +++ /dev/null @@ -1,74 +0,0 @@ -function downloadSubmodulesWithoutGit(submodules, branch) -if isempty(submodules) - return; -end -if isempty(branch) - return; -end - -for ii = 1:size(submodules,1) - url = submodules{ii,1}; - submodulepath = submodules{ii,3}; - - [~, submodulename] = fileparts(url); - filenameDownload = sprintf('./%s-%s', submodulename, branch); - - cleanup(filenameDownload, submodulepath); - - fprintf('Downloading %s/archive/refs/heads/%s.zip to %s\n', url, branch, [filenameDownload, '.zip']); - urlwrite(sprintf('%s/archive/refs/heads/%s.zip', url, branch), [filenameDownload, '.zip']); %#ok - - install(filenameDownload, submodulepath); - - fprintf('\n'); -end - - - - -% ---------------------------------------------------------- -function cleanup(filenameDownload, submodulepath) -removeFolderContents(submodulepath); -if ispathvalid_startup([filenameDownload, '.zip']) - fprintf('Removing %s\n', [filenameDownload, '.zip']); - try - delete([filenameDownload, '.zip']); - catch - warning('Failed to remove %s\n', [filenameDownload, '.zip']); - end -end -if ispathvalid_startup(filenameDownload) - fprintf('Removing %s\n', filenameDownload); - try - rmdir(filenameDownload, 's'); - catch - warning('Failed to remove %s\n', filenameDownload); - end -end - - - -% --------------------------------------------------------- -function install(filenameDownload, submodulepath) -if ispathvalid_startup([filenameDownload, '.zip']) - fprintf('Unzipping %s\n', [filenameDownload, '.zip']); - unzip([filenameDownload, '.zip']); -end -if ispathvalid_startup(filenameDownload) - fprintf('Copying %s/* to %s\n', filenameDownload, submodulepath); - copyFolderContents(filenameDownload, submodulepath); -end -if ispathvalid_startup([filenameDownload, '.zip']) - fprintf('Removing %s\n', [filenameDownload, '.zip']); - try - delete([filenameDownload, '.zip']); - catch - end -end -if ispathvalid_startup(filenameDownload) - fprintf('Removing %s\n', filenameDownload); - try - rmdir(filenameDownload, 's'); - catch - end -end diff --git a/Utils/submodules/gitSubmodulesInit.m b/Utils/submodules/gitSubmodulesInit.m deleted file mode 100644 index 86840d05..00000000 --- a/Utils/submodules/gitSubmodulesInit.m +++ /dev/null @@ -1,88 +0,0 @@ -function [cmds, errs, msgs] = gitSubmodulesInit(repo, options, preview) -cmds = {}; - -currdir = pwd; - -if ~exist('repo','var') || isempty(repo) - repo = [pwd, '/']; -end -if ~exist('options','var') - options = 'init'; -end -if ~exist('preview','var') - preview = false; -end - -repoFull = filesepStandard_startup(repo,'full'); -ii = 1; - -submodules = parseGitSubmodulesFile(repoFull); -url = gitGetOrigin(repoFull); -urlroot = fileparts(url); - -cmds{ii,1} = sprintf('cd %s', repoFull); ii = ii+1; -cmds{ii,1} = sprintf('git config --global http.sslverify "false"'); ii = ii+1; -for jj = 1:size(submodules,1) - [~, submodulename] = fileparts(submodules{jj,1}); - if ~strcmp(submodules{jj,1}, [urlroot, '/', submodulename]) - cmds{ii,1} = sprintf('git config --file=.gitmodules submodule.%s.url %s', submodules{jj,3}(1:end-1), [urlroot, '/', submodulename]); ii = ii+1; - end -end -cmds{ii,1} = sprintf('git submodule update --init --recursive --remote'); ii = ii+1; - -[errs, msgs] = exeShellCmds(cmds, preview); - -% Set origin for submodules to be same as origin for parent repo -for kk = 1:size(submodules,1) - [~, submodulename] = fileparts(submodules{kk,1}); - gitSetOrigin([repoFull, submodules{kk,3}], [urlroot, '/', submodulename]); -end - -% Checkout branch for parent repo and submodules. First checkout the source -% branch then destination. Source branch is the branch from which the -% destination branch is derived if destination branch is a new branch. -[branchSrc, branchDst] = gitGetSrcDstBranches(repoFull, options); -if isempty(branchSrc) || isempty(branchDst) - return -end -for ii = 1:size(submodules,1) - gitSetBranch([repoFull, submodules{ii,3}], branchSrc); -end -for ii = 1:size(submodules,1) - gitSetBranch([repoFull, submodules{ii,3}], branchDst); -end -gitSetBranch(repoFull, branchSrc); -gitSetBranch(repoFull, branchDst); - -cd(currdir); - - - - -% ---------------------------------------------------------------- -function [branchSrc, branchDst] = gitGetSrcDstBranches(repo, options) -branchSrc = ''; -branchDst = ''; -c = str2cell_startup(options, {':',','}); -for ii = 1:length(c) - c{ii} = strtrim(deblank(c{ii})); -end -if optionExists_startup(c, 'branch') - if length(c)==2 - branchSrc = gitGetBranch(repo); - branchDst = c{2}; - elseif length(c)==3 - if ~gitBranchExists(repo, c{2}) - h = msgbox(sprintf('ERROR: source branch ''%s'' does not exist in this repo. Source branch must exist to create the destination branch (''%s'')\n', ... - c{2}, c{3})); - waitForGui_startup(h); - return - end - branchSrc = c{2}; - branchDst = c{3}; - end -else - branchSrc = gitGetBranch(repo); - branchDst = branchSrc; -end - diff --git a/Utils/submodules/gitSubmodulesUpdate.m b/Utils/submodules/gitSubmodulesUpdate.m deleted file mode 100644 index a33fc3b8..00000000 --- a/Utils/submodules/gitSubmodulesUpdate.m +++ /dev/null @@ -1,28 +0,0 @@ -function [cmds, errs, msgs] = gitSubmodulesUpdate(repo, options, preview) -cmds = {}; - - -currdir = pwd; - -if ~exist('repo','var') || isempty(repo) - repo = [pwd, '/']; -end -if ~exist('options','var') - options = 'init'; -end -if ~exist('preview','var') - preview = false; -end - -repoFull = filesepStandard_startup(repo,'full'); - -ii = 1; - -cmds{ii,1} = sprintf('cd %s', repoFull); ii = ii+1; -cmds{ii,1} = sprintf('git config --global http.sslverify "false"'); ii = ii+1; -cmds{ii,1} = sprintf('git submodule update --init --recursive --remote'); ii = ii+1; - -[errs, msgs] = exeShellCmds(cmds, preview); - -cd(currdir); - diff --git a/Utils/submodules/resetSubmodules.m b/Utils/submodules/resetSubmodules.m deleted file mode 100644 index 07745b76..00000000 --- a/Utils/submodules/resetSubmodules.m +++ /dev/null @@ -1,17 +0,0 @@ -function resetSubmodules(repo) -% The main use of resetSubmodules is a tool to quickly reset the state -% of libraries when debugging issues in syncSubmodules -if ~exist('repo','var') || isempty(repo) - repo = pwd; -end -repoFull = filesepStandard_startup(repo,'full'); -submodules = parseGitSubmodulesFile(repoFull); -cd(repoFull); -for jj = 1:size(submodules,1) - fprintf('Reseting "%s":\n', submodules{jj,2}); - gitRevert(submodules{jj,2}); -end - - - - diff --git a/Utils/submodules/syncSubmodules.m b/Utils/submodules/syncSubmodules.m index 22cb945d..eb9832e7 100644 --- a/Utils/submodules/syncSubmodules.m +++ b/Utils/submodules/syncSubmodules.m @@ -13,13 +13,19 @@ % copy of the libraries. Default if argumenty not supplied is % the current folder % -% options: String argument with the possible values: {'init' | 'update'} +% options: String argument with the possible values: {'init' | 'update'}. Default if argument +% not supplied is 'update'. +% % 'init' - If standalone submodules have already been downloaded then do NOT % download again. Then sync the two libraries. % 'update' - If standalone submodules have already been downloaded then move % the current version to submodules.old and download again. Then % sync the two libraries -% Default if argument not supplied is 'init' +% 'parent2submodules' - Ignore last revision date and force direction of copying +% changes from parent repo to submodules. +% 'submodules2parent' - Ignore last revision date and force direction of copying +% changes from submodules to parent repo. +% % % preview: Boolean argument: if true, does not make any changes, default is preview % @@ -49,12 +55,16 @@ repo = [pwd, '/']; end if ~exist('options','var') - options = 'init'; + options = 'update'; end if ~exist('preview','var') preview = true; end +if ~optionExists(options, 'init') && ~optionExists(options, 'update') + options = [options, ':update']; +end + synctool.repoParentFull = filesepStandard_startup(repo,'full'); ii = 1; @@ -65,7 +75,7 @@ cmds{ii,1} = sprintf('cd %s', synctool.repoParentFull); ii = ii+1; for jj = 1:size(submodules,1) - submodulename = getRepos(submodules, jj); + submodulename = getRepos(submodules, jj, options); fprintf('Synching "%s" library:\n', submodulename); fprintf('Repo1: %s, %s\n', synctool.repo1.path, synctool.repo1.datetime.str); @@ -174,7 +184,7 @@ % ----------------------------------------------------------------------------------- -function submodulename = getRepos(submodules, jj) +function submodulename = getRepos(submodules, jj, options) global synctool synctool.repo1 = initRepo(); @@ -191,8 +201,16 @@ if r2(end)=='\' || r2(end)=='/' r2 = r2(1:end-1); end -[date1, dateS1] = getLastRevisionDate(synctool.repoParentFull, r1); -[date2, dateS2] = getLastRevisionDate(r2); +if optionExists(options, 'parent2submodules') + [date1, dateS1] = getCurrTime(); + [date2, dateS2] = getLastRevisionDate(r2); +elseif optionExists(options, 'submodules2parent') + [date1, dateS1] = getLastRevisionDate(synctool.repoParentFull, r1); + [date2, dateS2] = getCurrTime(); +else + [date1, dateS1] = getLastRevisionDate(synctool.repoParentFull, r1); + [date2, dateS2] = getLastRevisionDate(r2); +end status1 = hasChanges(r1); status2 = hasChanges(r2); @@ -261,37 +279,6 @@ -% -------------------------------------------------------------------------- -function status = hasChanges(repo) -status = 0; -[modified, added, deleted, untracked] = gitStatus(repo); -for ii = 1:length(modified) - c = str2cell(modified{ii}, ' '); - if isempty(strfind(c{2}, '..')) - status = 1; - end -end -for ii = 1:length(added) - c = str2cell(added{ii}, ' '); - if isempty(strfind(c{2}, '..')) - status = 1; - end -end -for ii = 1:length(deleted) - c = str2cell(deleted{ii}, ' '); - if isempty(strfind(c{2}, '..')) - status = 1; - end -end -for ii = 1:length(untracked) - c = str2cell(untracked{ii}, ' '); - if isempty(strfind(c{2}, '..')) - status = 1; - end -end - - - % ------------------------------------------------------------------------ function repo = initRepo(path, status, dateNum, dateStr) if nargin==0 diff --git a/Utils/submodules/updateSubmodulesWithoutGit.m b/Utils/submodules/updateSubmodulesWithoutGit.m deleted file mode 100644 index 568043d4..00000000 --- a/Utils/submodules/updateSubmodulesWithoutGit.m +++ /dev/null @@ -1,51 +0,0 @@ -function updateSubmodulesWithoutGit(submodules, branch) -if isempty(submodules) - return; -end -if isempty(branch) - return; -end - -for ii = 1:size(submodules,1) - url = submodules{ii,1}; - submodulepath = submodules{ii,3}; - - [~, submodulename] = fileparts(url); - filenameDownload = sprintf('./%s-%s', submodulename, branch); - - if ~isemptyFolder(submodulepath) - if ispathvalid_startup([submodulename, '.old/']) - fprintf('Deleteing folder %s\n', [submodulename, '.old/']); - rmdir([submodulename, '.old/'],'s') - end - fprintf('Moving %s to %s\n', submodulepath, [submodulename, '.old']); - copyFolderContents(submodulepath, [submodulename, '.old/']); - fprintf('Removing contents of %s\n', submodulepath); - removeFolderContents(submodulepath); - end - - if ispathvalid_startup([filenameDownload, '.zip']) - fprintf('Deleteing old zip file %s\n', [filenameDownload, '.zip']); - delete([filenameDownload, '.zip']); - end - - fprintf('Downloading %s/archive/refs/heads/%s.zip to %s\n', url, branch, [filenameDownload, '.zip']); - urlwrite(sprintf('%s/archive/refs/heads/%s.zip', url, branch), [filenameDownload, '.zip']); %#ok - - if ispathvalid_startup([filenameDownload, '.zip']) - fprintf('Unzipping %s\n', [filenameDownload, '.zip']); - unzip([filenameDownload, '.zip']); - end - - if ispathvalid_startup(filenameDownload) - fprintf('Copying %s/* to %s\n', filenameDownload, submodulepath); - copyFolderContents(filenameDownload, submodulepath); - else - fprintf('Moving %s to %s\n', [submodulename, '.old'], submodulepath); - movefile([submodulename, '.old'], submodulepath); - msgbox('Was not able download new version of "%s" and update it. Please download and updated manually.\n') - end - - fprintf('\n'); -end - diff --git a/Version.txt b/Version.txt index 45382aab..1f3171c7 100644 --- a/Version.txt +++ b/Version.txt @@ -1,3 +1 @@ -1.72.0 - - +1.76.0 \ No newline at end of file diff --git a/setpaths.m b/setpaths.m index 00545619..a0162b8c 100644 --- a/setpaths.m +++ b/setpaths.m @@ -1,10 +1,19 @@ -function setpaths(options) +function setpaths(addremove) % % USAGE: % +% % 1. Add all search paths for this repo % setpaths +% +% % 2. Same as 1.. Add all search paths for this repo % setpaths(1) +% +% % 3. Add all search paths for this repo while removing search +% % paths of all similar workspaces +% setpaths(2) +% +% % 4. Remove all search paths for this repo % setpaths(0) % currdir = pwd; @@ -17,22 +26,15 @@ function setpaths(options) appname = 'Homer3'; % Parse arguments - addremove = 1; - if ~exist('options','var') - options = ''; - elseif isnumeric(options) - if options == 0 - addremove = 0; - else - options = ''; - end + if ~exist('addremove','var') + addremove = 1; end % Add libraries on which Homer3 depends d = addDependenciesSearchPaths(); % Start logger only after adding library paths. Logger is in the Utils libary. - logger = InitLogger([], [pwd, '/setpaths']); + logger = Logger('setpaths'); % Create list of possible known similar apps that may conflic with current % app @@ -42,12 +44,16 @@ function setpaths(options) appThis = filesepStandard_startup(pwd); appThisPaths = findDotMFolders(appThis, exclSearchList); + if addremove == 0 if ~isempty(which('deleteNamespace.m')) deleteNamespace(appname); end removeSearchPaths(appThis); return; + elseif addremove == 2 + appNameExclList = [appNameExclList, appNameInclList]; + appNameInclList = {}; end appExclList = {}; @@ -116,7 +122,7 @@ function setpaths(options) end if ~isempty(which('setpaths_proprietary.m')) - setpaths_proprietary(options); + setpaths_proprietary(addremove); end warning('on','MATLAB:rmpath:DirNotFound'); @@ -152,6 +158,7 @@ function setpaths(options) % --------------------------------------------------- function setpermissions(appPath) if isunix() || ismac() + global logger if ~isempty(strfind(appPath, '/bin')) %#ok<*STREMP> cmd = sprintf('chmod 755 %s/*\n', appPath); logger.Write(cmd); @@ -234,9 +241,7 @@ function removeSearchPaths(app) end addSearchPaths(rootpath); end -if exist([pwd, '/Utils/submodules'],'dir') - addpath([pwd, '/Utils/submodules'],'-end'); -end + From 593737d38b4620ad9dae0ee5fa4203ad217cf621 Mon Sep 17 00:00:00 2001 From: kk1995 Date: Thu, 27 Jun 2024 11:52:09 -0400 Subject: [PATCH 3/3] Update hmrR_BlockAvg.m Update code for OD block average for compatibility with current SNIRF format --- FuncRegistry/UserFunctions/hmrR_BlockAvg.m | 38 ++++++++++++---------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/FuncRegistry/UserFunctions/hmrR_BlockAvg.m b/FuncRegistry/UserFunctions/hmrR_BlockAvg.m index 83bbdd03..3faaaa76 100644 --- a/FuncRegistry/UserFunctions/hmrR_BlockAvg.m +++ b/FuncRegistry/UserFunctions/hmrR_BlockAvg.m @@ -73,7 +73,7 @@ yblk = zeros(nPost-nPre+1,size(y,2),size(y,3),size(s,2)); elseif strcmp(datatype{1}, 'dOD') ml = data(kk).GetMeasList('reshape'); - yblk = zeros(nPost-nPre+1,size(y,2),size(s,2)); + yblk = zeros(nPost-nPre+1,size(y,2),size(y,3),size(s,2)); else return; end @@ -88,7 +88,7 @@ yblk(:,:,:,nBlk) = y(lstS(iT)+[nPre:nPost],:,:); %changed from yblk(:,:,:,end+1) elseif strcmp(datatype{1}, 'dOD') nBlk = nBlk + 1; - yblk(:,:,nBlk) = y(lstS(iT)+[nPre:nPost],:); % changd from yblk(:,:,end+1) + yblk(:,:,:,nBlk) = y(lstS(iT)+[nPre:nPost],:,:); % changd from yblk(:,:,end+1) end else fprintf('WARNING: Trial %d for Condition %d EXCLUDED because of time range\n',iT,iC); @@ -133,31 +133,35 @@ data_std(kk).AppendDataTimeSeries(ystd(:,:,:,iC)); data_sum2(kk).AppendDataTimeSeries(ysum2(:,:,:,iC)); elseif strcmp(datatype{1}, 'dOD') - yTrials(iC).yblk = yblk(:,:,1:nBlk); - yavg(:,:,iC) = mean(yblk(:,:,1:nBlk),3); - ystd(:,:,iC) = std(yblk(:,:,1:nBlk),[],3); + yTrials(iC).yblk = yblk(:,:,:,1:nBlk); + yavg(:,:,:,iC) = mean(yblk(:,:,:,1:nBlk),4); + ystd(:,:,:,iC) = std(yblk(:,:,:,1:nBlk),[],4); nTrials{kk}(iC) = nBlk; - % Loop over all wavelengths - for ii=1:size(yavg,2) - foom = ones(size(yavg,1),1)*mean(yavg(1:-nPre,ii,iC),1); - yavg(:,ii,iC) = yavg(:,ii,iC) - foom; + % Loop over all channels + for ii=1:size(yavg,3) + foom = ones(size(yavg,1),1)*mean(yavg(1:-nPre,:,ii,iC),1); + yavg(:,:,ii,iC) = yavg(:,:,ii,iC) - foom; for iBlk = 1:nBlk - yTrials(iC).yblk(:,ii,iBlk) = yTrials(iC).yblk(:,ii,iBlk) - foom; + yTrials(iC).yblk(:,:,ii,iBlk) = yTrials(iC).yblk(:,:,ii,iBlk) - foom; end - ysum2(:,ii,iC) = sum( yTrials(iC).yblk(:,ii,1:nBlk).^2 ,3); + ysum2(:,:,ii,iC) = sum( yTrials(iC).yblk(:,:,ii,1:nBlk).^2 ,4); % Snirf stuff: set channel descriptors - data_avg(kk).AddChannelDod(ml(ii,1), ml(ii,2), ml(ii,4), iC); - data_std(kk).AddChannelDod(ml(ii,1), ml(ii,2), ml(ii,4), iC); - data_sum2(kk).AddChannelDod(ml(ii,1), ml(ii,2), ml(ii,4), iC); + data_avg(kk).AddChannelDod(ml((ii-1)*2+1,1), ml((ii-1)*2+1,2), ml((ii-1)*2+1,4), iC); + data_std(kk).AddChannelDod(ml((ii-1)*2+1,1), ml((ii-1)*2+1,2), ml((ii-1)*2+1,4), iC); + data_sum2(kk).AddChannelDod(ml((ii-1)*2+1,1), ml((ii-1)*2+1,2), ml((ii-1)*2+1,4), iC); + + data_avg(kk).AddChannelDod(ml(ii*2,1), ml(ii*2,2), ml(ii*2,4), iC); + data_std(kk).AddChannelDod(ml(ii*2,1), ml(ii*2,2), ml(ii*2,4), iC); + data_sum2(kk).AddChannelDod(ml(ii*2,1), ml(ii*2,2), ml(ii*2,4), iC); end % Snirf stuff: set data vectors - data_avg(kk).AppendDataTimeSeries(yavg(:,:,iC)); - data_std(kk).AppendDataTimeSeries(ystd(:,:,iC)); - data_sum2(kk).AppendDataTimeSeries(ysum2(:,:,iC)); + data_avg(kk).AppendDataTimeSeries(yavg(:,:,:,iC)); + data_std(kk).AppendDataTimeSeries(ystd(:,:,:,iC)); + data_sum2(kk).AppendDataTimeSeries(ysum2(:,:,:,iC)); end end