[
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n.hypothesis/\n.pytest_cache/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# pyenv\n.python-version\n\n# celery beat schedule file\ncelerybeat-schedule\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n\n# MATLAB\n*.asv\n\n\n# LEAP-specific\ndata/*\n!data/readme.md\nmodels/*\nleap/toolbox/io/.lastdir\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "analysis/gait_analysis/compute_gait_densities.m",
    "content": "%% Pathing\nembed_path = 'Z:\\code\\2018-05-05_joints_tsne_FlyAging_talmo-labels\\results\\FlyAging-DiegoCNN_v1.0_filters=64_rot=15_lrfactor=0.1_lrmindelta=1e-05_03.mat';\ndensity_path = 'Z:\\code\\2018-05-05_joints_tsne_FlyAging_talmo-labels\\viz\\FlyAging-DiegoCNN_v1.0_filters=64_rot=15_lrfactor=0.1_lrmindelta=1e-05_03\\density.mat';\n\ndensity = load(density_path);\nembed = load(embed_path);\nY = embed.Y;\nLd = density.Ld;\ngait_path = 'C:\\code\\murthylab\\JointTracker\\LabelPostProcessing\\GaitVectors3.mat';\ngait = load(gait_path);\n%% Compute density and segment\nsigma = 20/30;\nnumGridPoints = 500;\ngridRange = [-20 20];\n\n% Setup grid\ngv = linspace(gridRange(1), gridRange(2), numGridPoints);\nxv = gv; yv = gv;\n\nD_tripod = getDensity(Y(gait.hmm.most_likely_seq == 3 & gait.moving_forward',:),sigma, numGridPoints, gridRange);\nD_tetrapod = getDensity(Y(gait.hmm.most_likely_seq == 4 & gait.moving_forward',:),sigma, numGridPoints, gridRange);\nD_NC = getDensity(Y(gait.hmm.most_likely_seq == 5 & gait.moving_forward',:),sigma, numGridPoints, gridRange);\n\ngait_density = zeros([size(D_tripod),3]);\ngait_density(:,:,1) = D_tripod;\ngait_density(:,:,2) = D_tetrapod;\ngait_density(:,:,3) = D_NC;\n\n% Saving\nsave_path = 'Gait_Densities';\nsave(save_path,'gait_density','D_tripod','D_tetrapod','D_NC','Ld');\n"
  },
  {
    "path": "analysis/gait_analysis/gait_analysis_computation.m",
    "content": "clear all;\n%% Pathing\n% addpath(genpath('deps'));\njoints_dir = 'Z:\\data\\JointTracker\\2018-02_FlyAging_boxes\\expts\\preds\\talmo-labels\\FlyAging-DiegoCNN_v1.0_filters=64_rot=15_lrfactor=0.1_lrmindelta=1e-05_03';\ndata_dir = 'Z:\\data\\Fly_Aging\\male_data';\n\n% Get the paths from the directories\ndata_fns = dir([data_dir,'/*_*']);\nexptnames = {data_fns(:).name};\n\njoint_fns = dir(strcat(joints_dir,'\\*.h5'));\njoint_exptnames = {joint_fns(:).name};\njoint_exptnames = cf(@(x) x(1:end-3),joint_exptnames);\n\njoint_expt_2_data = zeros(size(joint_exptnames));\nfor i = 1:numel(joint_exptnames)\n    for j = 1:numel(exptnames)\n        if strcmp(exptnames{j},joint_exptnames{i})\n           joint_expt_2_data(i) = j;\n        end\n    end\nend\n\njoint_paths = cell(size(joint_fns));\nfor i = 1:numel(joint_fns)\n    joint_paths{i} = fullfile(joint_fns(i).folder,joint_fns(i).name);\nend\n\ndata_paths = cell(size(data_fns));\nfor i = 1:numel(data_fns)\n    data_paths{i} = [fullfile(data_fns(i).folder,data_fns(i).name),'/Positions.dat'];\nend\n\n%% Get all of the positions for all of the videos. \npos = cell(size(joint_paths));\n% Loads the joint positions and adds the thorax back in to the fifth\n% feature as all zeros.\nparfor i = 1:numel(joint_paths)\n    joints = h5read(joint_paths{i},'/positions_pred');\n    joints = joints - joints(5,:,:);\n    joints = reshape(joints,[],size(joints,3));\n    pos{i} = joints;\nend\npos = cat(2,pos{:});\n\n%% Get the ids of all of the walking bouts according to the speed of centroids\nmoving_forward = cell(size(data_paths));\nforward_velocity = cell(size(data_paths));\nspeed = cell(size(data_paths));\nsmoothing_window = 5;\n\n% Velocity thresholds \nreconversion_constant = (1/(24.40/1088))./35; % This corrects a previous measurement error\nconversion_to_mm = 31.0857/1088;\nforward_motion_thresh = .02; % 2 mm/s\n\n% For each video, get all velocity and speed stats;\nfor i = 1:numel(data_paths)\n    % Load centers from ellipse data and smooth\n    frames = h5read(['Z:\\data\\JointTracker\\2018-02_FlyAging_boxes\\expts\\' exptnames{i} '.h5'],'/framesIdx');\n%     ell = h5read(data_paths{i},'/ell');\n    [X,Y,numLines] = positionReader(data_paths{i});\n    X = X.*reconversion_constant;\n    Y = Y.*reconversion_constant;% reconversion constants.\n    ctr = [X(frames),Y(frames)];\n    ctr = smoothdata(ctr,1,'movmean',smoothing_window);\n    ell = h5read(['Z:\\data\\JointTracker\\2018-02_FlyAging_boxes\\expts\\' exptnames{i} '.h5'],'/ell');\n    \n    % Get the velocity, direction of motion, and orientation of the fly\n    vel_ctr = diffpad(ctr);\n%     vel_ctr = smoothdata(vel_ctr,1,'movmean',smoothing_window*10);\n\n    direction_of_motion = smoothdata(mod(unwrap(atan2(vel_ctr(:,2),vel_ctr(:,1))),2*pi),'movmean',smoothing_window);\n    orientation = mod(unwrap((ell(frames,5)*2*pi/360)),2*pi);\n    difference_dir = abs(direction_of_motion-orientation);\n \n    % Get the component of the velocity in the forward direction\n    speed_ctr = sqrt(sum(vel_ctr.^2,2));\n    speed{i} = speed_ctr;\n    forward_velocity{i} = cos(difference_dir).*speed{i};\n    moving_forward{i} = forward_velocity{i} > forward_motion_thresh; \nend\nlengths = cellfun(@(x) numel(x),moving_forward);\nspeed = cat(1,speed{:});\nmoving_forward = cat(1,moving_forward{:});\nforward_velocity = cat(1,forward_velocity{:});\nfv = forward_velocity;\n\n%% Get rasters of when legs are moving in the forward direction\n% This is meant to replicate \"Quantification of \n% gait parameters in freely walking wild type and sensory deprived \n% Drosophila melanogaster\" Figure 4\nFs = 100;\n\n% We want to look at only the leg tips \ntips = [22 26 30 10 14 18]; % (The order matches the paper)\npos_tips = reshape(pos,[],2,size(pos,2));\npos_tips = pos_tips(tips,:,:);\ndim = 1; % Only look in the x direction \ntraj = squeeze(pos_tips(:,dim,:));\n\n% Get the velocity relative to the center of the fly (egocentric vel) \nvel = diffpad(traj,2);\nvel = smoothdata(vel,2,'gauss',smoothing_window);\n\n% Define stance to be when the legs move in the negative x direction. \nstance = vel<0;\n\n%% Get the contiguous bouts that will be used for HMM fitting\nseq = sum(stance)+1;\nTRGuess = rand(3);\nEMITGuess = rand(3,7);\nmf = find(moving_forward);\n\n% Get the frames of contiguous bouts\nendFrames = cumsum(lengths);\nstartFrames = [1 endFrames(1:end-1)'+1];\nsamples = cell1(numel(lengths));\nsamples_per_video = 3000;\nduration_thresh = 50;\nfor i = 1:numel(lengths)\n    bw = bwconncomp(moving_forward(startFrames(i):endFrames(i)));\n    duration = cellfun(@(x) numel(x),bw.PixelIdxList);\n    bw.PixelIdxList(duration < duration_thresh) = [];\n    ids = cat(1,bw.PixelIdxList{:});\n    ids = ids(1:(min(samples_per_video,numel(ids))));\n    samples{i} = startFrames(i) + ids - 1;\nend\nnum_samples = cellfun(@(x) numel(x),samples);\nsamples = cat(1,samples{num_samples>1});\n\n%% Train HMM and get most likely sequence\ntic;[ESTTR,ESTEMIT] = hmmtrain(seq(samples),TRGuess,EMITGuess);toc\nmls = hmmviterbi(seq,ESTTR,ESTEMIT);\n\n%% Here you should look at the emission probabilities\nfigure;\nimagesc(ESTEMIT);\n\n%% Reorder the labels to 3 = tripod, 4 = tetrapod, 5 = non-canonical\n% Note: this can be automated, but since initialization is random\n% and it doesn't take that long to train, I prefer checking manually. \nmls(mls == 3) = 4;\nmls(mls == 2) = 5;\nmls(mls == 1) = 3;\n\n%% Save results into a structure\nhmm.TRGUESS = TRGuess;\nhmm.EMITGUESS = EMITGuess;\nhmm.TrainingSamples = uint8(seq(samples));\nhmm.ESTTR = ESTTR;\nhmm.ESTEMIT = ESTEMIT;\nhmm.most_likely_seq = uint8(mls);\n\n% Saving\n% save_path = 'GaitVector3';\n% save(save_path,'hmm');\n\n%% Look at particular section\n% Limit the window to a particular region\n% Tripod = 17230751;\n% Tetrapod = 4781582;\n% Tetrapod = 8472800\nstart = 8472800;\nids = start:start+100;\n\n% Save example gait vectors\nexample_vel = vel(:,ids);\nexample_stance = stance(:,ids);\nexample_fv = fv(ids);\n\n% Saving\n% save_path = 'TripodExample';\n% save_path = 'TetrapodExample';\n% save(save_path,'example_vel','example_stance','example_fv','Fs');\n\n%% Calculate the distribution of speeds for each hidden state and save\nmls = hmm.most_likely_seq;\nspeed_lim = [2 35]; % mm/s\ntri_ids = moving_forward & (mls == 3)' & (fv.*Fs < speed_lim(2)) & (fv.*Fs > speed_lim(1));\ntetra_ids = moving_forward & (mls == 4)' & (fv.*Fs < speed_lim(2)) & (fv.*Fs > speed_lim(1));\nNC_ids = moving_forward & (mls == 5)' & (fv.*Fs < speed_lim(2)) & (fv.*Fs > speed_lim(1));\n\n\n[N1,edges1] = histcounts(fv(tri_ids).*Fs,166);\n[N2,edges2] = histcounts(fv(tetra_ids).*Fs,166);\n[N3,edges3] = histcounts(fv(NC_ids).*Fs,166);\n\n% Saving\n% save_path = 'Gait_Speed_Distributions';\n% save(save_path,'N1','N2','N3','edges1','edges2','edges3','speed_lim')\n\n%% Look at the Velocity statistics per Tsne locomotor state\ndensity_path = 'Z:\\code\\2018-05-05_joints_tsne_FlyAging_talmo-labels\\viz\\FlyAging-DiegoCNN_v1.0_filters=64_rot=15_lrfactor=0.1_lrmindelta=1e-05_03\\density.mat';\ndensity = load(density_path);\nembedding_ordered_locomotor_states = [7 11 13 10 8 9];\nnum_states = numel(embedding_ordered_locomotor_states);\n\n%% Calculate the velocity distributions.\nspeed_ids = fv > 0 & fv < .4;\nh = cell1(num_states);\nN = cell1(num_states);\nedges = cell1(num_states);\nfor i = 1:num_states\n    state_ids = density.YL == embedding_ordered_locomotor_states(i);\n    [N{i},edges{i}] = histcounts(fv(state_ids & speed_ids)*100,'Normalization','pdf');\nend\n\n% Saving\n% save_path = 'Cluster_Velocity_Distributions';\n% save(save_path,'N','edges','num_states');\n\n%% Calculate the velocity of legs during swing as you bin velocities differently\nwin = -1:5;\nspeed_levels = [2 5:5:45];\nleg_vel_at_speed = zeros(numel(speed_levels)-1,numel(win));\nleg_vel_std_at_speed = zeros(numel(speed_levels)-1,numel(win));\n\n% For each body speed level get the velocity of the swings over the window\nfor s = 1:numel(speed_levels)-1\n    swings = cell([size(vel,1) 1]);\n    inSpeed = (speed'*Fs >= speed_levels(s) & speed'*Fs < speed_levels(s+1));\n    % Get the swings in which the fly was at the speed level and moving\n    % forward.\n    parfor i = 1:size(vel,1)\n        % First get the swing starts\n        bw = bwconncomp(~stance(i,:));\n        ids = cell2mat(cf(@(x) x(1),bw.PixelIdxList));\n        swing_start = false(size(stance(i,:)));\n        swing_start(ids) = true;\n        % Then get the swing starts where the fly is moving forward within\n        % the speed threshold. \n        bw = bwconncomp(swing_start  & inSpeed);\n        ids = cell2mat(cf(@(x) x(1),bw.PixelIdxList));\n        ids = ids' + win;\n        vel_i = vel(i,:);\n        swings{i} = indpad(vel_i,ids);\n    end\n    swings = cat(1,swings{:});\n    leg_vel_at_speed(s,:) = nanmean(swings,1);\n    leg_vel_std_at_speed(s,:) = nanstd(swings,1);\nend\n\n% Saving\n% save_path = 'Swing_Velocity_Over_Time';\n% save(save_path,'leg_vel_at_speed','leg_vel_std_at_speed','speed_levels','win');\n\n%% Stance / Swing Duration\nswing_durations = cell([1,size(stance,1)]);\nstance_durations = cell([1,size(stance,1)]);\nswing_body_velocities = cell([1,size(stance,1)]);\nstance_body_velocities = cell([1,size(stance,1)]);\nperiod = cell([1,size(stance,1)]);\nperiod_velocities = cell([1,size(stance,1)]);\n\n% Get the swing, stance, and period duration and velocities\nparfor i = 1:size(stance,1)\n    % Swing\n    bw = bwconncomp(~stance(i,:));\n    swing_durations{i} = cellfun(@(x) numel(x),bw.PixelIdxList);\n    swing_body_velocities{i} = cellfun(@(x) mean(fv(x)),bw.PixelIdxList);\n    \n    % Stance\n    bw = bwconncomp(stance(i,:));\n    stance_durations{i} = cellfun(@(x) numel(x),bw.PixelIdxList);\n    stance_body_velocities{i} = cellfun(@(x) mean(fv(x)),bw.PixelIdxList);\n    \n    % Period\n%     bw = bwconncomp(stance(i,:));\n    period{i} = cellfun(@(x,y) numel(x(1):y(end)),{bw.PixelIdxList{1:end-1}},{bw.PixelIdxList{2:end}});\n    period_velocities{i} = cellfun(@(x,y) mean(fv(x(1):y(end))),{bw.PixelIdxList{1:end-1}},{bw.PixelIdxList{2:end}});\nend\n\nswing_durations = cat(2,swing_durations{:});\nstance_durations = cat(2,stance_durations{:});\nswing_body_velocities = cat(2,swing_body_velocities{:});\nstance_body_velocities = cat(2,stance_body_velocities{:});\nperiod = cat(2,period{:});\nperiod_velocities = cat(2,period_velocities{:});\n\n%% Plot as a line plot\nstance_vel_thresh = 7.2; % This number is taken from Mendes et al\n\nids = stance_body_velocities*Fs > stance_vel_thresh;\nxranges = [stance_vel_thresh  50];\nyranges = [0 prctile(stance_durations(ids),99)];\nstance_edges = xranges(1):1:xranges(2);\n\n% Stance Duration vs Velocity\nX1 = stance_body_velocities(ids)*Fs;\nY1 = stance_durations(ids);\n[X1ids] = discretize(X1,stance_edges);\n[stance_dur_mu, stance_dur_std] = grpstats(Y1, categorical(X1ids),{'mean','std'});\n\nswing_vel_thresh = 7.2; % This number is taken from Mendes et al\nids = swing_body_velocities*Fs > swing_vel_thresh;\nxranges = [swing_vel_thresh  50];\nswing_edges = xranges(1):1:xranges(2);\n\n% Stance Duration vs Velocity\nX2 = swing_body_velocities(ids)*Fs;\nY2 = swing_durations(ids);\n[X2ids] = discretize(X2,swing_edges);\n[swing_dur_mu, swing_dur_std] = grpstats(Y2, categorical(X2ids),{'mean','std'});\n\n% Saving\n% save_path = 'Swing_and_Stance_versus_Velocity';\n% save(save_path,'stance_dur_mu','swing_dur_mu','stance_dur_std','swing_dur_std','stance_edges','swing_edges')\n"
  },
  {
    "path": "analysis/gait_analysis/gait_analysis_plotting.m",
    "content": "clear all;\n%% Look at particular section\n% Pick the gait example to observe\n% load('TetrapodExample');\nload('TripodExample');\n\n% Plot the velocity of the leg tips\nfigure('pos',[153, 427, 560, 420]); hold on; axis tight; set(gcf,'color','w'); fontsize(16)\nimagesc(example_vel);\nxlabel('Time (seconds)')\nxticklabels(xticks/Fs);\nylabel('Leg Tip')\nyticks([1:6])\nyticklabels({'RF','RM','RH','LF','LM','LH'})\nax1 = gca;\ncaxis([-10 10]);\n\n% Plot the rasters\nfigure('pos',[850, 634, 848, 334]); hold on; axis tight; set(gcf,'color','w'); fontsize(16)\nimagesc(example_stance); colormap('gray');\nxlabel('Time (seconds)')\nxticklabels(xticks/Fs);\nylabel('Leg Tip')\nyticks([1:6])\nyticklabels({'LF','LM','LH','RF','RM','RH'})\nax2 = gca;\n\n% plot the forward velocity of the fly\nfigure('pos',[850, 359, 854, 186]); hold on; axis tight; set(gcf,'color','w'); fontsize(16)\nplot(example_fv.*Fs)\nxlabel('Time (seconds)')\nxticklabels(xticks/Fs);\nylabel('Forward Velocity (mm/s)')\nax3 = gca;\nylim([0 40]);\n\n% plot the raster of tripod, tetrapod, or non-canonical\nfigure('pos',[849, 114, 931, 150]); hold on; axis tight; set(gcf,'color','w'); fontsize(16)\nexample_gait = sum(example_stance,1);\nexample_gait(~(example_gait == 3 | example_gait == 4)) = 5; \nimagesc(example_gait);colormap('jet');h = colorbar; \nxlabel('Time (seconds)')\nxticklabels(xticks/Fs);\nyticks([])\nylabel(h,'Number of legs in stance')\nax4 = gca;\nlinkaxes([ax1,ax2,ax3,ax4],'x')\n\n%% Plot the emission probabilities for each hidden states\nload('GaitVectors3.mat');\nemissions = hmm.ESTEMIT;\ntemp = emissions(2,:);\nemissions(2,:) = emissions(3,:);\nemissions(3,:) = temp;\nfigure; hold on;\nimagesc(emissions);\nfor i = 1:size(emissions,1)\n    for j = 1:size(emissions,2)\n        caption = sprintf('%.2f',emissions(i,j));\n        text(j,i,caption,'Fontsize',10,'FontWeight','bold','HorizontalAlignment','center','Color',[0 0 0]);\n    end\nend\naxis ij;\naxis tight\nxlabel('Number of Legs in Stance');\nyticks([1 2 3]);\nyticklabels({'Tripod','Tetrapod','Non-canonical'})\nxticklabels({'0','1','2','3','4','5','6'})\nfontsize(16)\n\nfigure; hold on;\nplot(emissions','LineWidth',3);\nxlabel('Number of Legs in Stance');\nxticklabels({'0','1','2','3','4','5','6'})\nlegend({'Tripod','Tetrapod','Non-canonical'})\nylabel('Emission Probability')\n\n%% Plot the distribution of speeds\nload('Gait_Speed_Distributions');\nfigure; hold on;\nplot(edges1(1:end-1),N1,'LineWidth',3)\nplot(edges2(1:end-1),N2,'LineWidth',3)\nplot(edges3(1:end-1),N3,'LineWidth',3)\nxlabel('Forward Velocity (ms)')\nylabel('Count')\nxlim(speed_lim)\nlegend({'Tripod','Tetrapod','Non-canonical'})\nfontsize(16)\n\n%% Plot the velocity Distributions\nload('Cluster_Velocity_Distributions');\nfigure; hold on;\ncmap = spring(num_states);\nfor i = 1:num_states\n    plot(edges{i}(1:end-1),N{i},'Color',cmap(num_states + 1 -i,:),'LineWidth',3);\nend\ngrid on;\nxlabel('Forward Velocity')\nylabel('Probability')\naxis tight;\nylim([0 .2])\n\n%% Plotting the mean and std with bounded lines\nload('Swing_Velocity_Over_Time');\ncmap = parula(numel(speed_levels));\np_lines = cell1(numel(speed_levels)-1);\nfigure('pos',[568, 186, 1036, 798]); figclosekey; set(gcf,'color','w'); hold on;\nfor i = 1:size(leg_vel_at_speed,1)\n    yci = zeros(2,size(leg_vel_at_speed,2));\n    yci(1,:) = leg_vel_std_at_speed(i,:);\n    yci(2,:) = leg_vel_std_at_speed(i,:);\n    [p_lines{i},~] = boundedline(win*10,leg_vel_at_speed(i,:),yci','alpha','cmap',cmap(i,:));\n    p_lines{i}.LineWidth = 3;\nend\n\n% Legend\nleg = cell([1 numel(speed_levels)-1]);\nfor i = 1:numel(speed_levels)-1\n    leg{i} = sprintf('%d - %d mm/s',speed_levels(i),speed_levels(i+1));\nend\nl = legend([p_lines{:}],leg);\nl.Position = [0.7503 0.6488 0.1573 0.3239];\nfontsize(16)\nxlabel('Time from swing onset (ms)')\nylabel('Swing velocity (mm/s)')\n% export_fig('figs/Swing_Velocity_vs_Time_Confidences.png','-r300')\n\n%% Plot the Swing_and_Stance_versus_Velocity\nload('Swing_and_Stance_versus_Velocity')\nfigure; figclosekey, hold on;\n\nyci = zeros(2,numel(stance_dur_std));\nyci(1,:) = stance_dur_std;\nyci(2,:) = stance_dur_std;\n[bl1,~] = boundedline(stance_edges(2:end),stance_dur_mu',yci','alpha');\n\nyci = zeros(2,numel(swing_dur_std));\nyci(1,:) = swing_dur_std;\nyci(2,:) = swing_dur_std;\n[bl2,~] = boundedline(swing_edges(2:end),swing_dur_mu',yci','alpha','r');\n\nyticklabels(round(yticks*10))\nylabel('Durations (ms)');\nxlabel('Average Body Speed (mm/s)');\naxis tight\nlegend([bl1,bl2],{'Stance','Swing'})\nfontsize(16);"
  },
  {
    "path": "analysis/gait_analysis/plot_gait_densities.m",
    "content": "clear all;\n%% Plot all three densities overlayed ontop of one another\nload('Gait_Densities');\nfigure; hold on;\nh = imagesc(gait_density./(max(max(max(gait_density)))));\naxis equal; axis xy; axis off\n\n%% Plot the distributions for each mode in the same scale\n\nLocomotor_states = [7 11 13 9 10 8];\n[cropped,~,crop_mask] = bwcrop(ismember(Ld,Locomotor_states));\n\nfigure; hold on; axis xy; axis equal; axis off;\ncropped_density = reshape(D_tripod(crop_mask),size(cropped,1),[]);\nh = imagesc(cropped_density);\nc1 = colorbar;\npeak = max(c1.Limits);\ncolormap('viridis')\n\nfigure; hold on; axis xy; axis equal; axis off;\ncropped_density = reshape(D_tetrapod(crop_mask),size(cropped,1),[]);\nh = imagesc(cropped_density);\n% h.AlphaData = 1 .* ~reshape(density.Lbnds(crop_mask),size(cropped,1),[]);\nc2 = colorbar;\ncolormap('viridis')\ncaxis([0 peak]);\n\nfigure; hold on; axis xy; axis equal; axis off;\ncropped_density = reshape(D_NC(crop_mask),size(cropped,1),[]);\nh = imagesc(cropped_density);\n% h.AlphaData = 1 .* ~reshape(density.Lbnds(crop_mask),size(cropped,1),[]);\ncolormap('viridis')\nc3 = colorbar;\ncaxis([0 peak]);\n"
  },
  {
    "path": "data/readme.md",
    "content": "The full fly dataset and all trained networks used in our paper can be downloaded from: [`http://arks.princeton.edu/ark:/88435/dsp01pz50gz79z`](http://arks.princeton.edu/ark:/88435/dsp01pz50gz79z)\n\nAdditional datasets:\n\n## BermanFlies\n\n_We recommend using this for testing._\n\n- [Cluster sampled images](https://1drv.ms/u/s!AnmpIqqfwz3zgbUg2M8Sa_0NcLhrMg) (**68.7 MiB**)\n- [Labels for n = 1500 images](https://1drv.ms/u/s!AnmpIqqfwz3zgbUeIuYYDj_A1pCr9Q) (**291 KiB**)\n- [Trained network model](https://1drv.ms/u/s!AnmpIqqfwz3zgcwUvbqPUn7mXIMLLg) (**53.0 MiB**)\n- [Full dataset](http://arks.princeton.edu/ark:/88435/dsp01pz50gz79z) (**~168 GiB**)\n\n## CatNect\n\nThis is the dataset used for the tutorial. It contains a clip of a cat chasing a laser pointer recorded with a Kinect.\n\n- [Full clip](https://1drv.ms/u/s!AnmpIqqfwz3zgcwS_3gAJFU0sANBcA) (**105 MiB**)\n- [Cluster sampled images](https://1drv.ms/u/s!AnmpIqqfwz3zgcwR_9mmNJz8ALW3Hw) (**42.2 MiB**)\n- [Labels for n = 40 images](https://1drv.ms/u/s!AnmpIqqfwz3zgcwQlKSXXDy9KvIPVg) (**82.3 KiB**)\n- [Trained network model](https://1drv.ms/u/s!AnmpIqqfwz3zgc0H-v6qbnc3vak2Lw) (**64.7 MiB**)\n\n"
  },
  {
    "path": "examples/batch_process_video.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Example: Predict body part positions from an MP4 file\\n\",\n    \"This notebook presents an example pipeline for applying a trained LEAP network to ~360k frames read from an MP4 video.\\n\",\n    \"\\n\",\n    \"You can download the data to reproduce the benchmarking results below.\\n\",\n    \"\\n\",\n    \"**Input data:** [072212_163153.mp4](https://1drv.ms/v/s!AnmpIqqfwz3zgcgekCxNp-MN76p1UQ) (254 MiB)\\n\",\n    \"\\n\",\n    \"**Output data:** [072212_163153.preds.h5](https://1drv.ms/u/s!AnmpIqqfwz3zgcgdDhQrKRsBaxvCXQ) (46.9 MiB)\\n\",\n    \"\\n\",\n    \"The trained network can be [downloaded here](https://1drv.ms/u/s!AnmpIqqfwz3zgdpIOzsqojhEmr0J0w) or from the links in the [repository data folder](https://github.com/talmo/leap/tree/master/data).\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stderr\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"C:\\\\Anaconda3\\\\lib\\\\site-packages\\\\h5py\\\\__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\\n\",\n      \"  from ._conv import register_converters as _register_converters\\n\",\n      \"Using TensorFlow backend.\\n\"\n     ]\n    },\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"Platform: Windows-10-10.0.16299-SP0\\n\",\n      \"h5py:\\n\",\n      \"Summary of the h5py configuration\\n\",\n      \"---------------------------------\\n\",\n      \"\\n\",\n      \"h5py    2.7.1\\n\",\n      \"HDF5    1.10.1\\n\",\n      \"Python  3.6.4 |Anaconda, Inc.| (default, Jan 16 2018, 10:22:32) [MSC v.1900 64 bit (AMD64)]\\n\",\n      \"sys.platform    win32\\n\",\n      \"sys.maxsize     9223372036854775807\\n\",\n      \"numpy   1.14.1\\n\",\n      \"\\n\",\n      \"Keras: 2.2.0\\n\",\n      \"Tensorflow: 1.5.0\\n\",\n      \"Devices:\\n\",\n      \"[name: \\\"/device:CPU:0\\\"\\n\",\n      \"device_type: \\\"CPU\\\"\\n\",\n      \"memory_limit: 268435456\\n\",\n      \"locality {\\n\",\n      \"}\\n\",\n      \"incarnation: 12947954769568633288\\n\",\n      \", name: \\\"/device:GPU:0\\\"\\n\",\n      \"device_type: \\\"GPU\\\"\\n\",\n      \"memory_limit: 9143884186\\n\",\n      \"locality {\\n\",\n      \"  bus_id: 1\\n\",\n      \"}\\n\",\n      \"incarnation: 16757352010506659625\\n\",\n      \"physical_device_desc: \\\"device: 0, name: GeForce GTX 1080 Ti, pci bus id: 0000:01:00.0, compute capability: 6.1\\\"\\n\",\n      \", name: \\\"/device:GPU:1\\\"\\n\",\n      \"device_type: \\\"GPU\\\"\\n\",\n      \"memory_limit: 9143884186\\n\",\n      \"locality {\\n\",\n      \"  bus_id: 1\\n\",\n      \"}\\n\",\n      \"incarnation: 16188733693954377295\\n\",\n      \"physical_device_desc: \\\"device: 1, name: GeForce GTX 1080 Ti, pci bus id: 0000:02:00.0, compute capability: 6.1\\\"\\n\",\n      \"]\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"import os\\n\",\n    \"import numpy as np\\n\",\n    \"import cv2\\n\",\n    \"import h5py\\n\",\n    \"from time import time\\n\",\n    \"\\n\",\n    \"import keras\\n\",\n    \"import keras.models\\n\",\n    \"from leap.predict_box import convert_to_peak_outputs\\n\",\n    \"from leap.utils import versions\\n\",\n    \"\\n\",\n    \"versions(list_devices=True)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Parameters\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 2,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"# Media file path\\n\",\n    \"video_path = \\\"D:/tmp/072212_163153.mp4\\\"\\n\",\n    \"\\n\",\n    \"# Trained network path\\n\",\n    \"model_path = \\\"D:/OneDrive/code/leap/data/BermanFlies/models/180615_025354-n=1500/final_model.h5\\\"\\n\",\n    \"\\n\",\n    \"# Predictions output path\\n\",\n    \"save_path = \\\"D:/tmp/072212_163153.preds.h5\\\"\\n\",\n    \"\\n\",\n    \"# Number of frames to read before predicting (higher = faster, but limited by RAM)\\n\",\n    \"chunk_size = 10000\\n\",\n    \"\\n\",\n    \"# Number of frames to evaluate at once on the GPU (higher = faster, but limited by GPU memory)\\n\",\n    \"batch_size = 64\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Processing\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 3,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"Model: D:/OneDrive/code/leap/data/BermanFlies/models/180615_025354-n=1500/final_model.h5\\n\",\n      \"    Input: (None, 192, 192, 1)\\n\",\n      \"    Output: (None, 3, 32)\\n\",\n      \"Predicted: 10000/361000 frames | Elapsed: 0.4 min / 391.1 FPS / ETA: 15.0 min\\n\",\n      \"Predicted: 20000/361000 frames | Elapsed: 0.8 min / 435.0 FPS / ETA: 13.1 min\\n\",\n      \"Predicted: 30000/361000 frames | Elapsed: 1.1 min / 451.0 FPS / ETA: 12.2 min\\n\",\n      \"Predicted: 40000/361000 frames | Elapsed: 1.5 min / 459.0 FPS / ETA: 11.7 min\\n\",\n      \"Predicted: 50000/361000 frames | Elapsed: 1.8 min / 464.7 FPS / ETA: 11.2 min\\n\",\n      \"Predicted: 60000/361000 frames | Elapsed: 2.1 min / 468.7 FPS / ETA: 10.7 min\\n\",\n      \"Predicted: 70000/361000 frames | Elapsed: 2.5 min / 471.4 FPS / ETA: 10.3 min\\n\",\n      \"Predicted: 80000/361000 frames | Elapsed: 2.8 min / 473.5 FPS / ETA: 9.9 min\\n\",\n      \"Predicted: 90000/361000 frames | Elapsed: 3.2 min / 474.8 FPS / ETA: 9.5 min\\n\",\n      \"Predicted: 100000/361000 frames | Elapsed: 3.5 min / 476.0 FPS / ETA: 9.1 min\\n\",\n      \"Predicted: 110000/361000 frames | Elapsed: 3.8 min / 477.4 FPS / ETA: 8.8 min\\n\",\n      \"Predicted: 120000/361000 frames | Elapsed: 4.2 min / 478.4 FPS / ETA: 8.4 min\\n\",\n      \"Predicted: 130000/361000 frames | Elapsed: 4.5 min / 479.5 FPS / ETA: 8.0 min\\n\",\n      \"Predicted: 140000/361000 frames | Elapsed: 4.8 min / 481.4 FPS / ETA: 7.7 min\\n\",\n      \"Predicted: 150000/361000 frames | Elapsed: 5.2 min / 483.0 FPS / ETA: 7.3 min\\n\",\n      \"Predicted: 160000/361000 frames | Elapsed: 5.5 min / 483.7 FPS / ETA: 6.9 min\\n\",\n      \"Predicted: 170000/361000 frames | Elapsed: 5.9 min / 484.0 FPS / ETA: 6.6 min\\n\",\n      \"Predicted: 180000/361000 frames | Elapsed: 6.2 min / 484.1 FPS / ETA: 6.2 min\\n\",\n      \"Predicted: 190000/361000 frames | Elapsed: 6.5 min / 484.3 FPS / ETA: 5.9 min\\n\",\n      \"Predicted: 200000/361000 frames | Elapsed: 6.9 min / 484.6 FPS / ETA: 5.5 min\\n\",\n      \"Predicted: 210000/361000 frames | Elapsed: 7.2 min / 484.7 FPS / ETA: 5.2 min\\n\",\n      \"Predicted: 220000/361000 frames | Elapsed: 7.6 min / 484.6 FPS / ETA: 4.8 min\\n\",\n      \"Predicted: 230000/361000 frames | Elapsed: 7.9 min / 484.7 FPS / ETA: 4.5 min\\n\",\n      \"Predicted: 240000/361000 frames | Elapsed: 8.3 min / 484.7 FPS / ETA: 4.2 min\\n\",\n      \"Predicted: 250000/361000 frames | Elapsed: 8.6 min / 484.9 FPS / ETA: 3.8 min\\n\",\n      \"Predicted: 260000/361000 frames | Elapsed: 8.9 min / 485.0 FPS / ETA: 3.5 min\\n\",\n      \"Predicted: 270000/361000 frames | Elapsed: 9.3 min / 485.2 FPS / ETA: 3.1 min\\n\",\n      \"Predicted: 280000/361000 frames | Elapsed: 9.6 min / 485.3 FPS / ETA: 2.8 min\\n\",\n      \"Predicted: 290000/361000 frames | Elapsed: 10.0 min / 485.1 FPS / ETA: 2.4 min\\n\",\n      \"Predicted: 300000/361000 frames | Elapsed: 10.3 min / 485.1 FPS / ETA: 2.1 min\\n\",\n      \"Predicted: 310000/361000 frames | Elapsed: 10.7 min / 485.1 FPS / ETA: 1.8 min\\n\",\n      \"Predicted: 320000/361000 frames | Elapsed: 11.0 min / 485.3 FPS / ETA: 1.4 min\\n\",\n      \"Predicted: 330000/361000 frames | Elapsed: 11.3 min / 485.1 FPS / ETA: 1.1 min\\n\",\n      \"Predicted: 340000/361000 frames | Elapsed: 11.7 min / 485.2 FPS / ETA: 0.7 min\\n\",\n      \"Predicted: 350000/361000 frames | Elapsed: 12.0 min / 485.4 FPS / ETA: 0.4 min\\n\",\n      \"Predicted: 360000/361000 frames | Elapsed: 12.4 min / 485.6 FPS / ETA: 0.0 min\\n\",\n      \"Predicted: 361000/361000 frames | Elapsed: 12.4 min / 485.4 FPS / ETA: 0.0 min\\n\",\n      \"Finished predicting 361000 frames.\\n\",\n      \"    Prediction | Runtime: 11.55 min / 520.921 FPS\\n\",\n      \"    Reading    | Runtime: 0.78 min / 7726.516 FPS\\n\",\n      \"Saved: D:/tmp/072212_163153.preds.h5\\n\",\n      \"Total runtime: 12.4 mins\\n\",\n      \"Total performance: 483.585 FPS\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"t0_all = time()\\n\",\n    \"\\n\",\n    \"# Load model and convert to peak-coordinate output\\n\",\n    \"model = convert_to_peak_outputs(keras.models.load_model(model_path))\\n\",\n    \"print(\\\"Model:\\\", model_path)\\n\",\n    \"print(\\\"    Input:\\\", str(model.input_shape))\\n\",\n    \"print(\\\"    Output:\\\", str(model.output_shape))\\n\",\n    \"\\n\",\n    \"# model = keras.utils.multi_gpu_model(model, gpus=2)\\n\",\n    \"\\n\",\n    \"# Open video for reading\\n\",\n    \"reader = cv2.VideoCapture(video_path)\\n\",\n    \"num_samples = int(reader.get(cv2.CAP_PROP_FRAME_COUNT))\\n\",\n    \"\\n\",\n    \"# Initialize\\n\",\n    \"positions_pred = []\\n\",\n    \"conf_pred = []\\n\",\n    \"buffer = []\\n\",\n    \"samples_predicted = 0\\n\",\n    \"reading_runtime = 0\\n\",\n    \"prediction_runtime = 0\\n\",\n    \"done = False\\n\",\n    \"\\n\",\n    \"# Process video chunk-by-chunk\\n\",\n    \"while not done:\\n\",\n    \"    t0_reading = time()\\n\",\n    \"    # Read and finish if no frame was retrieved\\n\",\n    \"    returned_frame, I = reader.read()\\n\",\n    \"    done = not returned_frame\\n\",\n    \"    reading_runtime += time() - t0_reading\\n\",\n    \"    \\n\",\n    \"    # Add current frame to buffer\\n\",\n    \"    if not done:\\n\",\n    \"        buffer.append(I[...,0])\\n\",\n    \"    \\n\",\n    \"    # Do we have anything to predict?\\n\",\n    \"    if len(buffer) >= chunk_size or (done and len(buffer) > 0):\\n\",\n    \"        t0_prediction = time()\\n\",\n    \"        \\n\",\n    \"        # Predict on buffer\\n\",\n    \"        Y = model.predict(np.stack(buffer, axis=0)[...,None], batch_size=batch_size)\\n\",\n    \"        \\n\",\n    \"        # Save\\n\",\n    \"        positions_pred.append(Y[:,:2,:].astype(\\\"int32\\\"))\\n\",\n    \"        conf_pred.append(Y[:,2,:].squeeze())\\n\",\n    \"        \\n\",\n    \"        # Empty out buffer container\\n\",\n    \"        buffer = []\\n\",\n    \"        \\n\",\n    \"        # Performance stats\\n\",\n    \"        samples_predicted += len(Y)\\n\",\n    \"        prediction_runtime += time() - t0_prediction\\n\",\n    \"        elapsed = time() - t0_all\\n\",\n    \"        fps = samples_predicted / elapsed\\n\",\n    \"        print(\\\"Predicted: %d/%d frames | Elapsed: %.1f min / %.1f FPS / ETA: %.1f min\\\" %\\n\",\n    \"              (samples_predicted, num_samples, elapsed / 60, fps, (num_samples - samples_predicted) / fps / 60))\\n\",\n    \"        \\n\",\n    \"# Close video reader\\n\",\n    \"reader.release()\\n\",\n    \"\\n\",\n    \"# Merge arrays\\n\",\n    \"positions_pred = np.concatenate(positions_pred, axis=0)\\n\",\n    \"conf_pred = np.concatenate(conf_pred, axis=0)\\n\",\n    \"\\n\",\n    \"# Report performance stats\\n\",\n    \"print(\\\"Finished predicting %d frames.\\\" % samples_predicted)\\n\",\n    \"print(\\\"    Prediction | Runtime: %.2f min / %.3f FPS\\\" % (prediction_runtime / 60, samples_predicted / prediction_runtime))\\n\",\n    \"print(\\\"    Reading    | Runtime: %.2f min / %.3f FPS\\\" % (reading_runtime / 60, samples_predicted / reading_runtime))\\n\",\n    \"\\n\",\n    \"# Save\\n\",\n    \"if os.path.exists(save_path):\\n\",\n    \"    os.remove(save_path)\\n\",\n    \"with h5py.File(save_path, \\\"w\\\") as f:\\n\",\n    \"        f.attrs[\\\"num_samples\\\"] = num_samples\\n\",\n    \"        f.attrs[\\\"video_path\\\"] = video_path\\n\",\n    \"        f.attrs[\\\"model_path\\\"] = model_path\\n\",\n    \"\\n\",\n    \"        ds_pos = f.create_dataset(\\\"positions_pred\\\", data=positions_pred, compression=\\\"gzip\\\", compression_opts=1)\\n\",\n    \"        ds_pos.attrs[\\\"description\\\"] = \\\"coordinate of peak at each sample\\\"\\n\",\n    \"        ds_pos.attrs[\\\"dims\\\"] = \\\"(sample, [x, y], joint) === (sample, [column, row], joint)\\\"\\n\",\n    \"\\n\",\n    \"        ds_conf = f.create_dataset(\\\"conf_pred\\\", data=conf_pred, compression=\\\"gzip\\\", compression_opts=1)\\n\",\n    \"        ds_conf.attrs[\\\"description\\\"] = \\\"confidence map value in [0, 1.0] at peak\\\"\\n\",\n    \"        ds_conf.attrs[\\\"dims\\\"] = \\\"(sample, joint)\\\"\\n\",\n    \"\\n\",\n    \"        total_runtime = time() - t0_all\\n\",\n    \"        f.attrs[\\\"reading_runtime_secs\\\"] = reading_runtime\\n\",\n    \"        f.attrs[\\\"prediction_runtime_secs\\\"] = prediction_runtime\\n\",\n    \"        f.attrs[\\\"total_runtime_secs\\\"] = total_runtime\\n\",\n    \"        \\n\",\n    \"    \\n\",\n    \"print(\\\"Saved:\\\", save_path)\\n\",\n    \"\\n\",\n    \"print(\\\"Total runtime: %.1f mins\\\" % (total_runtime / 60))\\n\",\n    \"print(\\\"Total performance: %.3f FPS\\\" % (samples_predicted / total_runtime))\"\n   ]\n  }\n ],\n \"metadata\": {\n  \"kernelspec\": {\n   \"display_name\": \"Python 3\",\n   \"language\": \"python\",\n   \"name\": \"python3\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 3\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython3\",\n   \"version\": \"3.6.4\"\n  },\n  \"toc\": {\n   \"nav_menu\": {},\n   \"number_sections\": true,\n   \"sideBar\": true,\n   \"skip_h1_title\": false,\n   \"toc_cell\": false,\n   \"toc_position\": {},\n   \"toc_section_display\": \"block\",\n   \"toc_window_display\": false\n  }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 2\n}\n"
  },
  {
    "path": "examples/hdf5tovid.m",
    "content": "% Clean start!\nclear all, clc\n\n%% Parameters\n% Path to input file\ndataPath = '../data/examples/072212_163153.clip.h5';\n\n% Dataset name\ndset = '/box';\n\n% Path to output file\nsavePath = 'C:\\tmp\\072212_163153.clip.mp4';\n\n% Frames to convert at a time (lower this if your memory is limited)\nchunkSize = 1000;\n\n% Framerate for playback of the video file\nfps = 25;\n\n%% Initialize\n% Get dataset info\ninfo = h5info(dataPath, dset);\nshape = info.Dataspace.Size;\nnumFrames = shape(end);\n\n% Check if file already exists\nif exist(savePath,'file') > 0\n    warning(['Overwriting existing video file: ' savePath])\n    delete(savePath)\nend\n\n% Open video for writing\nwriter = VideoWriter(savePath,'MPEG-4'); % use this for MP4s\n% writer = VideoWriter(savePath,'Motion JPEG AVI'); % use this for AVIs\n\n% Set compression quality (higher = bigger file, better quality)\nwriter.Quality = 100;\n\n% Set playback speed in frames/second\nwriter.FrameRate = fps;\n\n% Open file for writing\nwriter.open();\n\n%% Save\nframesWritten = 0;\ndone = false;\nt0 = tic;\nwhile ~done\n    % Check how many frames to read\n    chunkFrames = min(chunkSize, numFrames - framesWritten);\n    \n    % Read chunk\n    chunk = h5read(dataPath,dset,[1 1 1 framesWritten+1], [inf inf inf chunkFrames]);\n    \n    % Check for datatype/range concordance (floats must be in [0,1])\n    if isfloat(chunk) && max(chunk(:)) > 1\n        chunk = chunk / 255;\n    end\n    \n    % Write frames\n    writer.writeVideo(chunk);\n\n    % Increment frames written counter\n    framesWritten = framesWritten + size(chunk,4);\n    \n    % Check if we're done\n    done = framesWritten >= numFrames;\nend\n\nelapsed = toc(t0);\nfprintf('Finished writing %d frames in %.2f mins:\\n\\t%s\\n', framesWritten, elapsed/60, savePath)\n\n% Close file\nwriter.close();\n"
  },
  {
    "path": "examples/vidtohdf5.m",
    "content": "% Clean start!\nclear all, clc\n\n%% Parameters\n% Path to input file\nvideoPath = '..\\..\\leap\\data\\examples\\072212_163153.clip.mp4';\n\n% Path to output file\n% savePath = '..\\..\\leap\\data\\examples\\072212_163153.clip.h5';\nsavePath = 'C:\\tmp\\072212_163153.clip.h5';\n\n% Frames to convert at a time (lower this if your memory is limited)\nchunkSize = 1000;\n\n% Convert frames to single channel grayscale images (instead of 3 channel RGB)\ngrayscale = true;\n\n%% Initialize\n% Open video for reading\nvr = VideoReader(videoPath);\n\n% Check size from first frame\nI0 = vr.readFrame();\nif grayscale; I0 = rgb2gray(I0); end\nframeSize = size(I0);\nif numel(frameSize) == 2; frameSize = [frameSize 1]; end\n\n% Reset VideoReader\ndelete(vr);\nvr = VideoReader(videoPath);\n\n% Check if file already exists\nif exist(savePath,'file') > 0\n    warning(['Overwriting existing HDF5 file: ' savePath])\n    delete(savePath)\nend\n\n% Create HDF5 file with infinite number of frames and GZIP compression\nh5create(savePath,'/box',[frameSize inf],'ChunkSize',[frameSize 1],'Deflate',1,'Datatype','uint8')\n\n%% Save\nbuffer = cell(chunkSize,1);\ndone = false;\nframesRead = 0;\nframesWritten = 0;\nt0 = tic;\nwhile ~done\n    % Read next frame\n    I = vr.readFrame();\n    if grayscale; I = rgb2gray(I); end\n    \n    % Check if there are any frames left\n    done = ~vr.hasFrame();\n    \n    % Increment frames read counter and add to the write buffer\n    framesRead = framesRead + 1;\n    buffer{mod(framesRead-1, chunkSize)+1} = I;\n    \n    % Have we filled the buffer or are there no frames left?\n    if mod(framesRead, chunkSize) == 0 || done\n        % Concatenate the buffer into an array\n        chunk = cat(4, buffer{:});\n        \n        % Extend the dataset and save to disk\n        h5write(savePath, '/box', chunk, [1 1 1 framesWritten+1], size(chunk))\n        \n        % Increment frames written counter\n        framesWritten = framesWritten + size(chunk,4);\n    end\nend\nelapsed = toc(t0);\nfprintf('Finished writing %d frames in %.2f mins.\\n', framesWritten, elapsed/60)\n\nh5disp(savePath)\n"
  },
  {
    "path": "install_leap.m",
    "content": "function install_leap()\n%INSTALL_LEAP Installs the Python package and adds MATLAB scripts to path.\n% Usage:\n%   install_leap\n% \n% See also: uninstall_leap, test_leap\n\n% Find base repository path (where this file is contained)\nbasePath = fileparts(which('install_leap'));\n\n% Add to MATLAB path\naddpath(genpath(fullfile(basePath,'leap')));\n\n% Check if Python package is importable\ncanImportPython = test_leap();\nif ~canImportPython\n    [status,msg] = system(['pip install -e \"' basePath '\"']);\n    disp(msg)\nend\n\n% Check again\ntest_leap;\nend\n"
  },
  {
    "path": "leap/__init__.py",
    "content": "from . import image_augmentation\nfrom . import layers\nfrom . import models\nfrom . import predict_box\nfrom . import training\nfrom . import utils\nfrom . import viz"
  },
  {
    "path": "leap/compute_errors.m",
    "content": "function err = compute_errors(pos_pred, pos_gt)\n%COMPUTE_ERRORS Computes error rates given predicted and ground truth positions.\n% Usage:\n%   err = compute_errors(pos_pred, pos_gt)\n% \n% Args:\n%   pos_pred: predicted positions    (J x 2 x N)\n%   pos_gt: ground truth predictions (J x 2 x N)\n%\n% Returns:\n%   err: struct with error metrics\n% \n% See also: \n\nif isstruct(pos_pred)\n    pos_pred =  pos_pred.positions_pred;\nend\nif isstruct(pos_gt)\n    pos_gt = pos_gt.positions_pred;\nend\n\n% Find the difference between predicted and ground truth\ndelta = pos_pred - pos_gt;\n\n% Find Euclidean distance between each pair of points\neuclidean = squeeze(sqrt(sum(delta .^ 2, 2)))';\n\n% Compute metrics overall\nmae_all = mean(abs(delta(:)));\nmse_all = mean(delta(:) .^ 2);\nrmse_all = sqrt(mse_all);\n\n% Compute metrics per joint\ndelta_rows = reshape(permute(delta,[2 3 1]),[],size(delta,1));\nmae = mean(abs(delta_rows));\nmse = mean(delta_rows .^ 2);\nrmse = sqrt(mse);\n\n% Return everything\nerr = varstruct(delta, euclidean, ...\n    mae_all, mse_all, rmse_all, ...\n    delta_rows, mae, mse, rmse);\n\nend\n"
  },
  {
    "path": "leap/confmaps2pts.m",
    "content": "function [pts, confvals] = confmaps2pts(C)\n%CONFMAPS2PTS Convert a set of confidence maps into a set of points.\n% Usage:\n%   [pts, confvals] = confmaps2pts(C)\n%\n% See also: pts2confmaps\n\nnumChannels = size(C,3);\npts = zeros(numChannels,2,'single');\nconfvals = zeros(numChannels,1,'like',C);\nfor i = 1:numChannels\n    [confvals(i), ind] = max(vert(C(:,:,i)));\n    [r,c] = ind2sub(size(C(:,:,i)),ind);\n    pts(i,:) = [c r];\nend\n\nend\n\n"
  },
  {
    "path": "leap/generate_training_set.m",
    "content": "function savePath = generate_training_set(boxPath, varargin)\n%GENERATE_TRAINING_SET Creates a dataset for training.\n% Usage: generate_training_set(boxPath, ...)\n\nt0_all = stic;\n%% Setup\ndefaults = struct();\ndefaults.savePath = [];\ndefaults.scale = 1;\ndefaults.mirroring = true; % flip images and adjust confidence maps to augment dataset\ndefaults.horizontalOrientation = true; % animal is facing right/left if true (for mirroring)\ndefaults.sigma = 5; % kernel size for confidence maps\ndefaults.normalizeConfmaps = true; % scale maps to [0,1] range\ndefaults.postShuffle = true; % shuffle data before saving (useful for reproducible dataset order)\ndefaults.testFraction = 0; % separate these data from training and validation sets\ndefaults.compress = false; % use GZIP compression to save the outputs\n\nparams = parse_params(varargin,defaults);\n\n% Paths\nlabelsPath = repext(boxPath,'labels.mat');\n\n% Output\nsavePath = params.savePath;\nif isempty(savePath)\n    savePath = ff(fileparts(boxPath), 'training', [get_filename(boxPath,true) '.h5']);\n    savePath = get_new_filename(savePath,true);\nend\nmkdirto(savePath)\n\n%% Labels\nlabels = load(labelsPath);\n\n% Check for complete frames\nlabeledIdx = find(squeeze(all(all(~isnan(labels.positions),2),1)));\nnumFrames = numel(labeledIdx);\nprintf('Found %d/%d labeled frames.', numFrames, size(labels.positions,3))\n\n% Pull out label data\njoints = labels.positions(:,:,labeledIdx);\njoints = joints * params.scale;\nnumJoints = size(joints,1);\n\n% Pull out other info\njointNames = labels.skeleton.nodes;\nskeleton = struct();\nskeleton.edges = labels.skeleton.edges;\nskeleton.pos = labels.skeleton.pos;\n\n%% Load images\nstic;\nbox = h5readframes(boxPath,'/box',labeledIdx);\nif params.scale ~= 1; box = imresize(box,params.scale); end\nboxSize = size(box(:,:,:,1));\nstocf('Loaded %d images', size(box,4))\n\n% Load metadata\ntry exptID = h5read(boxPath, '/exptID'); exptID = exptID(labeledIdx); catch; end\ntry framesIdx = h5read(boxPath, '/framesIdx'); framesIdx = framesIdx(labeledIdx); catch; end\ntry idxs = h5read(boxPath, '/idxs'); idxs = idxs(labeledIdx); catch; end\n\ntry L = h5read(boxPath, '/L'); L = L(labeledIdx); catch; end\ntry box_no_seg = imresize(h5readframes(boxPath,'/box_no_seg',labeledIdx),params.scale); catch; end\ntry box_raw = imresize(h5readframes(boxPath,'/box_raw',labeledIdx),params.scale); catch; end\nattrs = h5att2struct(boxPath);\n\n%% Generate confidence maps\nstic;\nconfmaps = NaN([boxSize(1:2), numJoints, numFrames],'single');\nparfor i = 1:numFrames\n    pts = joints(:,:,i);\n    confmaps(:,:,:,i) = pts2confmaps(pts,boxSize(1:2),params.sigma,params.normalizeConfmaps);\nend\nstocf('Generated confidence maps') % 15 sec for 192x192x32x500\nvarsize(confmaps)\n\n%% Augment by mirroring\nif params.mirroring\n    % Flip images\n    if params.horizontalOrientation\n        box_flip = flipud(box);\n        try box_no_seg_flip = flipud(box_no_seg); catch; end\n        try box_raw_flip = flipud(box_raw); catch; end\n        confmaps_flip = flipud(confmaps);\n        joints_flip = joints; joints_flip(:,2,:) = size(box,1) - joints_flip(:,2,:);\n    else\n        box_flip = fliplr(box);\n        try box_no_seg_flip = fliplr(box_no_seg); catch; end\n        try box_raw_flip = fliplr(box_raw); catch; end\n        confmaps_flip = fliplr(confmaps);\n        joints_flip = joints; joints_flip(:,1,:) = size(box,2) - joints_flip(:,1,:);\n    end\n\n    % Check for *L/*R naming pattern (e.g., {{'wingL','wingR'}, {'legR1','legL1'}})\n    swap_names = {};\n    baseNames = regexp(jointNames,'(.*)L([0-9]*)$','tokens');\n    isSymmetric = ~cellfun(@isempty,baseNames);\n    for i = horz(find(isSymmetric))\n        nameR = [baseNames{i}{1}{1} 'R' baseNames{i}{1}{2}];\n        if ismember(nameR,jointNames)\n            swap_names{end+1} = {jointNames{i}, nameR};\n        end\n    end\n\n    % Swap channels accordingly\n    printf('Symmetric channels:')\n    for i = 1:numel(swap_names)\n        [~,swap_idx] = ismember(swap_names{i}, jointNames);\n        if any(swap_idx == 0); continue; end\n        printf('    %s (%d) <-> %s (%d)', jointNames{swap_idx(1)}, swap_idx(1), ...\n            jointNames{swap_idx(2)}, swap_idx(2))\n\n        joints_flip(swap_idx,:,:) = joints_flip(fliplr(horz(swap_idx)),:,:);\n        confmaps_flip(:,:,swap_idx,:) = confmaps_flip(:,:,fliplr(horz(swap_idx)),:);\n    end\n\n    % Merge\n    [box,flipped] = cellcat({box,box_flip},4);\n    joints = cat(3, joints, joints_flip);\n    try box_raw = cat(4,box_raw,box_raw_flip); catch; end\n    try box_no_seg = cat(4,box_no_seg,box_no_seg_flip); catch; end\n    confmaps = cat(4, confmaps, confmaps_flip);\n\n    labeledIdx = [labeledIdx(:); labeledIdx(:)];\n    try exptID = [exptID(:); exptID(:)]; catch; end\n    try framesIdx = [framesIdx(:); framesIdx(:)]; catch; end\n    try idxs = [idxs(:); idxs(:)]; catch; end\n    \n    % Update frame count\n    numFrames = size(box,4);\nend\n\n%% Post-shuffle\nshuffleIdx = vert(1:numFrames);\nif params.postShuffle\n    shuffleIdx = randperm(numFrames);\n    box = box(:,:,:,shuffleIdx);\n    labeledIdx = labeledIdx(shuffleIdx);\n    try box_no_seg = box_no_seg(:,:,:,shuffleIdx); catch; end\n    try box_raw = box_raw(:,:,:,shuffleIdx); catch; end\n    try exptID = exptID(shuffleIdx); catch; end\n    try framesIdx = framesIdx(shuffleIdx); catch; end\n    joints = joints(:,:,shuffleIdx);\n    confmaps = confmaps(:,:,:,shuffleIdx);\nend\n\n%% Separate testing set\nnumTestFrames = round(numel(shuffleIdx) * params.testFraction);\nif numTestFrames > 0\n    testIdx = randperm(numel(shuffleIdx),numTestFrames);\n    trainIdx = setdiff(shuffleIdx, testIdx);\n\n    % Test set\n    testing = struct();\n    testing.shuffleIdx = shuffleIdx(testIdx);\n    testing.box = box(:,:,:,testIdx);\n    testing.labeledIdx = labeledIdx(testIdx);\n    try testing.box_no_seg = box_no_seg(:,:,:,testIdx); catch; end\n    try testing.box_raw = box_raw(:,:,:,testIdx); catch; end\n    try testing.exptID = exptID(testIdx); catch; end\n    try testing.framesIdx = framesIdx(testIdx); catch; end\n    testing.joints = joints(:,:,testIdx);\n    testing.confmaps = confmaps(:,:,:,testIdx);\n    testing.testIdx = testIdx;\n\n    % Training set\n    shuffleIdx = shuffleIdx(trainIdx);\n    box = box(:,:,:,trainIdx);\n    labeledIdx = labeledIdx(trainIdx);\n    try box_no_seg = box_no_seg(:,:,:,trainIdx); catch; end\n    try box_raw = box_raw(:,:,:,trainIdx); catch; end\n    try exptID = exptID(trainIdx); catch; end\n    try framesIdx = framesIdx(trainIdx); catch; end\n    joints = joints(:,:,trainIdx);\n    confmaps = confmaps(:,:,:,trainIdx);\nend\n\n%% Save\n% Augment metadata\nattrs.createdOn = datestr(now);\nattrs.boxPath = boxPath;\nattrs.labelsPath = labelsPath;\nattrs.scale = params.scale;\nattrs.postShuffle = uint8(params.postShuffle);\nattrs.horizontalOrientation = uint8(params.horizontalOrientation);\n\n% Write\nstic;\nif exists(savePath); delete(savePath); end\n\n% Training data\nh5save(savePath,box,[],'compress',params.compress)\nh5save(savePath,labeledIdx)\nh5save(savePath,shuffleIdx)\ntry h5save(savePath,box_no_seg,[],'compress',params.compress); catch; end\ntry h5save(savePath,box_raw,[],'compress',params.compress); catch; end\ntry h5save(savePath,exptID); catch; end\ntry h5save(savePath,framesIdx); catch; end\nh5save(savePath,joints,[],'compress',params.compress)\nh5save(savePath,confmaps,[],'compress',params.compress)\n\n% Testing data\nif numTestFrames > 0\n    h5save(savePath,trainIdx)\n    h5savegroup(savePath,testing,'/testing','compress',params.compress)\nend\n\n% Metadata\nh5writeatt(savePath,'/confmaps','sigma',params.sigma)\nh5writeatt(savePath,'/confmaps','normalize',uint8(params.normalizeConfmaps))\nh5struct2att(savePath,'/',attrs)\nh5savegroup(savePath,skeleton,'/skeleton')\nh5writeatt(savePath,'/skeleton','jointNames',strjoin(jointNames,'\\n'))\n\nstocf('Saved:\\n%s', savePath)\nget_filesize(savePath)\n\n\nstocf(t0_all, 'Finished generating training set.');\nend"
  },
  {
    "path": "leap/graph2paf.m",
    "content": "function paf = graph2paf(nodes, edges, sz, channelsOnly, sigma)\n%GRAPH2PAF Converts a set of edges into part affinity fields.\n% Usage:\n%   graph2paf(nodes, edges, sz, sigma)\n% \n% Args:\n%   nodes: set of points (N x 2)\n%   edges: indices into nodes defining directed edges (E x 2)\n%   sz: grid/image size (1 x 2)\n%   channelsOnly: stack all PAFs along channels (dim 3) instead of dim 4 (default: true)\n%   sigma: maximum distance from edge to keep (default: 5)\n% \n% Returns:\n%   paf: part affinity fields (sz(1) x sz(2) x 2E) or (sz(1) x sz(2) x 2 x E)\n% \n% See also: pts2confmaps\n\nif nargin < 4 || isempty(channelsOnly); channelsOnly = true; end\nif nargin < 5 || isempty(sigma); sigma = 5; end\n\n% Create image coordinate grid\n[XX,YY] = meshgrid(1:sz(2), 1:sz(1));\n\n% Create PAFs for each edge\nE = size(edges,1);\npaf = cell(E,1);\nfor i = 1:E\n    % Pull out edge points\n    src = nodes(edges(i,2),:);\n    dst = nodes(edges(i,1),:);\n    \n    % Edge length\n    L = norm(dst - src, 2);\n\n    % Unit vectors\n    V = (dst - src) ./ L; % pointing along edge\n    Vp = [-V(:,2), V(:,1)]; % perpendicular\n\n    % Signed distance along edge\n    D1 = sum(V .* ([XX(:) YY(:)] - src),2);\n\n    % Absolute distance orthogonal to edge\n    D2 = abs(sum(Vp .* ([XX(:) YY(:)] - src),2));\n\n    % Vector field mask\n    paf_mask = reshape(D1 >= 0 & D1 <= L & D2 <= sigma, sz);\n\n    % Create vector field along channels (X and Y)\n    paf{i} = paf_mask .* permute(V, [1 3 2]);\nend\n\n% Merge all edge PAFs\nif channelsOnly\n    paf = cat(3, paf{:});\nelse\n    paf = cat(4, paf{:});\nend\n\nend\n"
  },
  {
    "path": "leap/guis/label_joints.m",
    "content": "function label_joints(boxPath, skeletonPath)\n%LABEL_JOINTS GUI to click on images to yield a graph.\n% Usage:\n%   label_joints(boxPath)\n%   label_joints(boxPath, skeletonPath)\n%\n% See also: make_template_skeleton\n\n\n\n%% Startup\n% addpath(genpath('deps'))\n\n% Ask for path to data file\nif nargin < 1 || isempty(boxPath); boxPath = uibrowse('*.h5',[],'Select box HDF5 file'); end\n\n% Params\nif nargin < 2; skeletonPath = []; end\nrecreate_labels = nargin > 1; % force recreate labels file\n\n% Settings (saved in *.labels.mat file)\nglobal config;\nconfig = struct();\nconfig.dsetName = '/box';\nconfig.nodeSize = 10; % size of draggable markers\nconfig.defaultNodeColor = [1 0 0]; % default color of movable nodes\nconfig.initializedNodeColor = [1 1 0]; % color of initialized nodes\nconfig.labeledNodeColor = [0 1 0]; % color of movable nodes with user input\nconfig.initialFrame = 1; % first frame displayed\nconfig.shuffleFrames = false; % shuffle frame order\nconfig.autoSave = true; % save before going to a new frame\nconfig.clickNearest = false; % true = click moves nearest node; false = selected node\nconfig.draggable = true; % false = cannot drag joint markers\nconfig.altArrowsToMoveNodes = true; % false = arrow keys move nodes, alt+arrows changes frames\nconfig.zoomBoxFrames = [-250, 250]; % number of frames in the status zoomed in box (pre, post)\nconfig.imgFigPos = [835 341 709 709]; % main labeling figure window\nconfig.ctrlFigPos = [1545 342 374 708]; % control/reference window\nconfig.statusFigPos = [836 33 1081 277]; % status bars and settings window\n\n%%\n% Initialize labeling session\nbox = [];\nnumNodes = [];\nnumFrames = [];\nnumLabeled = [];\nglobal labels;\n\n% Loads or creates *.labels.mat and populates config\ninitializeLabels();\n\n% Pre-shuffle frames for shuffle mode\nshuffleIdx = randperm(numFrames);\n\n% Set status colormap colors\nstatusCmap = {\n    config.defaultNodeColor\n    config.initializedNodeColor\n    config.labeledNodeColor\n    };\nfor k = 1:numel(statusCmap)\n    if ischar(statusCmap{k}); statusCmap{k} = colorCode2rgb(statusCmap{k}); end\nend\nstatusCmap = cellcat(statusCmap,1);\n\n% Zoom box convenience (compute window)\nif isscalar(config.zoomBoxFrames); config.zoomBoxFrames = round([-0.5 0.5] .* config.zoomBoxFrames); end\nzoomBoxWindow = config.zoomBoxFrames(1):config.zoomBoxFrames(2);\n\n    function initializeLabels()\n        labels = struct();\n\n        % Metadata\n        labels.boxPath = boxPath;\n        labels.savePath = repext(boxPath, '.labels.mat');\n\n        % Ask for path to skeleton file\n        if isequal(skeletonPath,true) || ~exists(labels.savePath)\n            skeletonPath = uibrowse('*.mat',[],'Select skeleton MAT file');\n        end\n\n        % Open box file\n        box = h5file(boxPath, config.dsetName);\n        numFrames = size(box,4);\n\n        stic;\n        if ~exists(labels.savePath) || recreate_labels\n            % Load template skeleton\n            labels.skeletonPath = skeletonPath;\n            labels.skeleton = load(skeletonPath);\n\n            % Initialize custom defaults container\n            labels.initialization = NaN(numel(labels.skeleton.nodes), 2, numFrames, 'single');\n\n            % Try using initialization built into the HDF5 file\n            try\n                labels.initialization = h5read(boxPath, '/initialization');\n                labels.initialization_metadata = h5att2struct(boxPath, '/initialization');\n\n                printf('Using pre-initialized joint predictions.')\n            catch\n            end\n\n            % Initialize user labels\n            labels.positions = NaN(numel(labels.skeleton.nodes), 2, numFrames, 'single');\n\n            % Settings\n            labels.config = config;\n\n            % Timestamps\n            labels.createdOn = datestr(now);\n            labels.lastModified = datestr(now);\n\n            % Initialize history\n            labels.session = 1;\n            addToHistory(\"Created labels file.\");\n\n            % Create labels file\n            save(labels.savePath, '-struct', 'labels', '-v7.3')\n            stocf('Created labels file: %s', labels.savePath)\n        else\n            % Load\n            labels = load(labels.savePath);\n\n            % Update paths\n            labels.boxPath = boxPath;\n            labels.savePath = repext(boxPath, '.labels.mat');\n\n            % Update config\n            if isfield(labels,'config')\n                config = parse_params(labels.config,config);\n            else\n                labels.config = config;\n            end\n\n            if ~isfield(labels,'session')\n                labels.session = 1;\n            else\n                labels.session = labels.session + 1;\n            end\n\n            stocf('Loaded existing labels file: %s', labels.savePath)\n        end\n        addToHistory('Started session.')\n\n        % Convenience\n        numNodes = numel(labels.skeleton.nodes);\n    end\n\n    function addToHistory(message)\n    % Utility for adding a timestamped message to the history log\n\n        session = labels.session;\n        timestamp = datetime();\n        message = string(message);\n        historyItem = table(session, timestamp, message);\n        disp(historyItem)\n\n        if ~isfield(labels,'history') || isempty(labels.history)\n            labels.history = historyItem;\n        else\n            labels.history = [labels.history; historyItem];\n        end\n    end\n\n%% GUI\n% Build GUI\nglobal ui;\ninitializeGUI();\n    function initializeGUI()\n        ui = struct();\n\n        % %%%% Controls figure %%%%\n        ui.ctrl = struct();\n        ui.ctrl.fig = figure('NumberTitle','off','MenuBar','none', ...\n            'Name','LEAP Label GUI', 'WindowKeyPressFcn', @keyPress, 'DeleteFcn', @quit, ...\n            'Position', config.ctrlFigPos);\n        ui.ctrl.hbox = uix.HBox('Parent', ui.ctrl.fig);\n\n        % Joints panel\n        ui.ctrl.jointsPanel = uix.Panel('Parent',ui.ctrl.hbox, 'Title', 'Joints', 'Padding',5);\n        ui.ctrl.jointsList = uicontrol(ui.ctrl.jointsPanel, 'Style', 'listbox', 'String', labels.skeleton.joints.name, ...\n            'Callback',@(h,~,~)selectNode(h.Value));\n\n        % Reference image\n        ui.ctrl.refPanel = uix.Panel('Parent',ui.ctrl.hbox, 'Title', 'Reference', 'Padding',5);\n        ui.ctrl.refAx = axes(uicontainer('Parent',ui.ctrl.refPanel));\n        ui.ctrl.refImg = imagesc(labels.skeleton.refI);\n\n        % Style\n        ui.ctrl.refAx.Units = 'normalized';\n        ui.ctrl.refAx.Position = [0 0 1 1];\n        axis(ui.ctrl.refAx,'equal','tight','ij')\n        colormap(ui.ctrl.refAx,'gray')\n        noticks(ui.ctrl.refAx)\n        hold(ui.ctrl.refAx,'on')\n\n        % Plot reference skeleton\n        for i = 1:size(labels.skeleton.segments,1)\n            % Find default position of each nodes in the segment\n            pos = labels.skeleton.pos(labels.skeleton.segments.joints_idx{i},:);\n\n            % Plot\n            plot(ui.ctrl.refAx, pos(:,1), pos(:,2), '.-', 'Color',labels.skeleton.segments.color{i}, 'LineWidth', 1);\n        end\n\n        % Draw each joint node\n        ui.ctrl.refNodes = gobjects(height(labels.skeleton.joints),1);\n        for i = 1:numel(ui.ctrl.refNodes)\n            pos = labels.skeleton.joints.pos(i,:);\n            ui.ctrl.refNodes(i) = plot(ui.ctrl.refAx, pos(1),pos(2),'o', 'Color','r');\n        end\n\n        % Set box widths\n        ui.ctrl.hbox.Widths = [-1 -3];\n        %%%%\n\n        % %%%% Image figure %%%%\n        ui.img = struct();\n        ui.img.fig = figure('NumberTitle','off','MenuBar','none','ToolBar','none', ...\n            'Name',sprintf('Frame %d/%d', config.initialFrame, numFrames), 'WindowKeyPressFcn', @keyPress, 'DeleteFcn', @quit, ...\n            'Position', config.imgFigPos);\n        ui.img.ax = axes(ui.img.fig);\n        ui.img.img = imagesc(ui.img.ax, box(:,:,:,1));\n        ui.img.img.ButtonDownFcn = @(~,~) clickImage();\n\n        % Full figure image axes\n        ui.img.ax.Units = 'normalized';\n        ui.img.ax.Position = [0 0 1 1];\n\n        % Style\n        axis(ui.img.ax,'equal','tight','ij')\n        colormap(ui.img.ax,'gray')\n        noticks(ui.img.ax)\n        hold(ui.img.ax,'on')\n\n        % Initialize skeleton drawing container\n        ui.skel = struct();\n        ui.skel.segs = [];\n        ui.skel.nodes = [];\n        %%%%\n\n\n        % %%%% Status figure %%%%\n        % Initialize status container\n        ui.status = struct();\n        ui.status.selectedNode = [];\n        ui.status.movedNodes = false(numNodes,1);\n        ui.status.currentFrame = config.initialFrame;\n        ui.status.unsavedChanges = false(numFrames,1);\n        ui.status.initialPos = [];\n\n        % Get full status indicators for all frames\n        status = getStatus();\n        numInitialized = sum(all(status == 1,1));\n        numLabeled = sum(all(status == 2,1));\n\n        % Create figure window\n        ui.status.fig = figure('NumberTitle','off','MenuBar','none','ToolBar','none', ...\n            'Name',sprintf('Status: %d/%d (%.2f%%) labeled', numLabeled, numFrames, numLabeled/numFrames*100), ...\n            'WindowKeyPressFcn', @keyPress, 'DeleteFcn', @quit, ...\n            'Position', config.statusFigPos);\n        ui.status.hbox = uix.HBox('Parent', ui.status.fig, 'Padding',3);\n\n        % Status panel (left)\n        ui.status.statusPanel = uix.Panel('Parent',ui.status.hbox, 'Title','Status', 'Padding',5);\n        ui.status.statusBoxes = uix.VBox('Parent', ui.status.statusPanel);\n\n        % Status text\n        ui.status.stats = uix.VBox('Parent',ui.status.statusBoxes);\n\n        ui.status.framesInitialized = uicontrol(ui.status.stats,'Style','text','HorizontalAlignment','left',...\n            'String',sprintf('Initialized: %d/%d (%.3f%%)', numInitialized, numFrames, numInitialized/numFrames*100));\n        ui.status.framesLabeled = uicontrol(ui.status.stats,'Style','text','HorizontalAlignment','left',...\n            'String',sprintf('Labeled: %d/%d (%.3f%%)', numLabeled, numFrames, numLabeled/numFrames*100));\n        ui.status.stats.Heights = ones(1, numel(ui.status.stats.Children)) * 15;\n\n        % Status bars\n        ui.status.fullAx = axes(uicontainer('Parent',ui.status.statusBoxes));\n        ui.status.fullImg = imagesc(ui.status.fullAx, 1:numFrames, 1:numNodes, status, 'ButtonDownFcn', @clickStatusbar);\n        axis(ui.status.fullAx,'tight','ij')\n        hold(ui.status.fullAx,'on');\n        zoomBoxIdx = zoomBoxWindow + ui.status.currentFrame;\n        zoomBoxPts = [\n            zoomBoxIdx(1) 0\n            zoomBoxIdx(end) 0\n            zoomBoxIdx(end) numNodes\n            zoomBoxIdx(1) numNodes\n            zoomBoxIdx(1) 0\n            ];\n        ui.status.fullZoomBox = patch(ui.status.fullAx, zoomBoxPts(:,1),zoomBoxPts(:,2),'w','PickableParts','none');\n        ui.status.fullZoomBox.FaceAlpha = 0.25;\n        ui.status.fullZoomBox.EdgeColor = 'w';\n        colormap(ui.status.fullAx, statusCmap)\n        caxis(ui.status.fullAx,[0 2])\n        ui.status.fullAx.XLim = [-0.5 0.5] + [1 numFrames];\n        ui.status.fullAx.YLim = [-0.5 0.5] + [1 numNodes];\n%         ui.status.fullAx.YTick = 1:numNodes;\n%         ui.status.fullAx.YTickLabel = labels.skeleton.nodes;\n%         ui.status.fullAx.YAxis.TickLabelInterpreter = 'none';\n\n        % Status bars (zoomed)\n        ui.status.zoomAx = axes(uicontainer('Parent',ui.status.statusBoxes));\n        ui.status.zoomImg = imagesc(ui.status.zoomAx, zoomBoxIdx, 1:numNodes, zeros(numNodes,numel(zoomBoxIdx)), 'ButtonDownFcn', @clickStatusbar);\n        axis(ui.status.zoomAx,'tight','ij')\n        colormap(ui.status.zoomAx, statusCmap)\n        caxis(ui.status.zoomAx,[0 2])\n        ui.status.zoomAx.YLim = [-0.5 0.5] + [1 numNodes];\n%         ui.status.zoomAx.YTick = 1:numNodes;\n%         ui.status.zoomAx.YTickLabel = labels.skeleton.nodes;\n%         ui.status.zoomAx.YAxis.TickLabelInterpreter = 'none';\n\n        % Set UI heights\n        ui.status.statusBoxes.Heights = [sum(ui.status.stats.Heights)+5 -1 -1];\n\n        % Settings panel (right)\n        ui.status.configPanel = uix.Panel('Parent',ui.status.hbox, 'Title','Settings','Padding',5);\n        ui.status.configButtons = uix.VBox('Parent',ui.status.configPanel);\n\n        % Auto-save\n        uicontrol(ui.status.configButtons,'Style','checkbox','Value',config.autoSave, ...\n            'Callback',@(h,~)setConfig('autoSave',h.Value), ...\n            'String','Autosave labels','TooltipString','Automatically saves changes to disk when changing frames or exiting.');\n\n        % Shuffle frame order\n        uicontrol(ui.status.configButtons,'Style','checkbox','Value',config.shuffleFrames, ...\n            'Callback',@(h,~)setConfig('shuffleFrames',h.Value), ...\n            'String','Shuffle frame order','TooltipString','Shuffled order is fixed within this session. Uncheck to use file ordering.');\n\n        % Click nearest\n        uicontrol(ui.status.configButtons,'Style','checkbox','Value',config.clickNearest, ...\n            'Callback',@(h,~)setConfig('clickNearest',h.Value), ...\n            'String','Click to move nearest joint','TooltipString','If unchecked, clicking on the image moves the currently selected joint.');\n\n        % Draggable markers\n        uicontrol(ui.status.configButtons,'Style','checkbox','Value',config.draggable, ...\n            'Callback', @(h,~)toggleDraggableMarkers(h.Value), ...\n            'String','Draggable markers','TooltipString','If unchecked, joint markers can only be moved by clicking or keyboard.');\n\n        % Alt + arrows to move nodes\n        uicontrol(ui.status.configButtons,'Style','checkbox','Value',config.altArrowsToMoveNodes, ...\n            'Callback', @(h,~)setConfig('altArrowsToMoveNodes',h.Value), ...\n            'String','Alt + arrow keys move markers','TooltipString','If unchecked, move markers with Alt + arrow keys, change frames with arrow keys.');\n\n        % Export confidence maps\n        uicontrol(ui.status.configButtons,'Style','pushbutton', ...\n            'Callback', @(h,~)generateTrainingSet(), ...\n            'String','Generate training set','TooltipString','Creates a test set with confidence maps for training a network.');\n\n        % Fast training\n        uicontrol(ui.status.configButtons,'Style','pushbutton', ...\n            'Callback', @(h,~)fastTrain(), ...\n            'String','Fast train network','TooltipString','Trains a network for initialization using fast presets.');\n\n        % Initialization from predictions\n        uicontrol(ui.status.configButtons,'Style','pushbutton', ...\n            'Callback', @(h,~)predictInitializations(), ...\n            'String','Initialize with trained model','TooltipString','Generates predictions for all frames and uses it as initialization.');\n\n\n        % Set UI sizes\n        ui.status.configButtons.Heights = ones(1, numel(ui.status.configButtons.Children)) * 25;\n        ui.status.hbox.Widths = [-1 175];\n\n        % Give focus back to main image window\n        figure(ui.img.fig);\n    end\n    function toggleDraggableMarkers(TF)\n    % Sets whether joint markers are draggable using the mouse\n\n        if TF\n            set(ui.skel.nodes,'PickableParts','visible');\n            draggable(ui.skel.nodes, @nodesMoved, 'endFcn', @nodesMoveEnd);\n        else\n            draggable(ui.skel.nodes, 'off');\n            set(ui.skel.nodes,'PickableParts','none');\n        end\n        setConfig('draggable',TF);\n    end\n    function setConfig(configField, value)\n    % Helper to set config fields to specified value\n        config.(configField) = value;\n    end\n\n    function quit(h,~)\n    % Quit callback to close all windows simultaneously\n        % Log to history\n        addToHistory(\"Finished session.\")\n\n        % Save\n        if config.autoSave && isequal(h, ui.img.fig)\n            saveLabels();\n        end\n\n        % Delete figs\n        delete(ui.img.fig)\n        delete(ui.ctrl.fig)\n        delete(ui.status.fig)\n    end\n\n    function keyPress(~,evt)\n    % Hotkeys\n        % exclusive modifier flags:\n        noModifier = isempty(evt.Modifier);\n        shiftOnly = isequal(evt.Modifier, {'shift'});\n        ctrlOnly = isequal(evt.Modifier, {'control'});\n        altOnly = isequal(evt.Modifier, {'alt'});\n\n        % non-exclusive:\n        altPressed = ismember({'alt'}, evt.Modifier);\n        ctrlPressed = ismember({'control'}, evt.Modifier);\n        shiftPressed = ismember({'shift'}, evt.Modifier);\n\n        switch evt.Key\n            case 'q'\n                delete(ui.img.fig)\n            case 's'\n                saveLabels()\n            case 'r'\n                if noModifier % current node\n                    resetNodes(ui.status.selectedNode);\n                elseif shiftOnly % all nodes\n                    resetNodes();\n                end\n            case 'd'\n                if noModifier % current node\n                    setNodesToDefault(ui.status.selectedNode);\n                elseif shiftOnly % all nodes\n                    setNodesToDefault();\n                end\n            case 'tab'\n                if noModifier\n                    selectNode(mod(ui.status.selectedNode-1+1, numNodes) + 1);\n                elseif shiftOnly\n                    selectNode(mod(ui.status.selectedNode-1-1, numNodes) + 1);\n                end\n            case 'downarrow'\n                dXY = [0 1];\n                if (config.altArrowsToMoveNodes && altPressed) || ~config.altArrowsToMoveNodes\n                    if noModifier\n                        nudgeNode(dXY)\n                    elseif shiftOnly\n                        nudgeNode(dXY * 5)\n                    elseif ctrlOnly\n                        nudgeSegment(dXY)\n                    end\n                end\n            case 'uparrow'\n                dXY = [0 -1];\n                if (config.altArrowsToMoveNodes && altPressed) || ~config.altArrowsToMoveNodes\n                    if noModifier\n                        nudgeNode(dXY)\n                    elseif shiftOnly\n                        nudgeNode(dXY * 5)\n                    elseif ctrlOnly\n                        nudgeSegment(dXY)\n                    end\n                end\n            case 'leftarrow'\n                if (config.altArrowsToMoveNodes && altPressed) || (~config.altArrowsToMoveNodes && ~altPressed)\n                    dXY = [-1 0] - (shiftPressed * 4);\n                    if ctrlPressed; nudgeSegment(dXY);\n                    else; nudgeNode(dXY); end\n                else\n                    dt = -1 - (shiftPressed * 4);\n                    if config.shuffleFrames\n                        idx = find(shuffleIdx == ui.status.currentFrame);\n                        goToFrame(shuffleIdx(mod(idx-1+dt, numFrames) + 1))\n                    else\n                        goToFrame(mod(ui.status.currentFrame-1+dt, numFrames) + 1)\n                    end\n                end\n            case 'rightarrow'\n                if (config.altArrowsToMoveNodes && altPressed) || (~config.altArrowsToMoveNodes && ~altPressed)\n                    dXY = [1 0] + (shiftPressed * 4);\n                    if ctrlPressed; nudgeSegment(dXY);\n                    else; nudgeNode(dXY); end\n                else\n                    dt = 1 + (shiftPressed * 4);\n                    if config.shuffleFrames\n                        idx = find(shuffleIdx == ui.status.currentFrame);\n                        goToFrame(shuffleIdx(mod(idx-1+dt, numFrames) + 1))\n                    else\n                        goToFrame(mod(ui.status.currentFrame-1+dt, numFrames) + 1)\n                    end\n                end\n            case 'space'\n                % Get labeling status for all frames\n                labeled = getStatus() == 2;\n\n                % Consider current joint only if shift is pressed\n                if shiftPressed; labeled = labeled(ui.status.selectedNode,:);\n                else; labeled = all(labeled,1); end\n\n                % Find unlabeled frames excluding current frame\n                unlabeledIdxs = setdiff(find(labeled), ui.status.currentFrame);\n\n                if ~isempty(unlabeledIdxs)\n                    if ctrlPressed\n                        % Go to random unlabeled frame\n                        goToFrame(datasample(unlabeledIdxs,1));\n                    else\n                        % Go to first unlabeled frame\n                        goToFrame(unlabeledIdxs(1))\n                    end\n\n                end\n            case 'g'\n                % go to frame dialog\n%                 if ctrlOnly\n                % TODO:\n                %   - custom dialog box that starts focused on the textbox\n                %     and returns after pressing Enter/Esc\n                answer = inputdlg('Skip to frame index:','Skip to frame',1,{num2str(ui.status.currentFrame)});\n                try\n                    idx = round(str2double(answer));\n                    if idx >= 1 && idx <= numFrames\n                        goToFrame(idx);\n                    end\n                catch\n                end\n%                 end\n            case 'f'\n                markAllCorrect();\n            otherwise\n%                 evt\n        end\n    end\n\n    function clickImage()\n    % Callback to image clicks (but not on nodes)\n        % Pull out clicked point coordinate\n        pt = ui.img.ax.CurrentPoint(1,1:2);\n\n        % Get current node positions\n        pos = getNodePositions();\n\n        if config.clickNearest\n            % Find nearest node location\n            i = argmin(rownorm(pos - pt));\n        else\n            % Use current selection\n            i = ui.status.selectedNode;\n        end\n\n        % Update node position\n        pos(i,:) = pt;\n        updateSkeleton(pos);\n\n    end\n\n    function clickStatusbar(h,evt)\n    % Callback for seeking via mouse-click on the status bars\n        if evt.Button == 1\n            idx = clip(round(evt.IntersectionPoint(1)),[1 numFrames]);\n            goToFrame(idx);\n        end\n    end\n\n    function status = getStatus(idx)\n    % Utility function that checks labels for completeness status\n    % Returns [numJoints x numel(idx)] matrix with values:\n    %   0: default\n    %   1: initialized\n    %   2: labeled\n\n        % Get status for all frames by default\n        if nargin < 1; idx = 1:numFrames; end\n\n        % Initialize as default (0)\n        status = zeros(numNodes, numel(idx));\n\n        % Check for initialization\n        isInitialized = squeeze(all(~isnan(labels.initialization(:,:,idx)),2));\n        status(isInitialized) = 1;\n\n        % Check for user labels\n        isLabeled = squeeze(all(~isnan(labels.positions(:,:,idx)),2));\n        status(isLabeled) = 2;\n    end\n\n%% Training and dataset generation\n    function predictInitializations(modelPath)\n    % Generates predictions for the entire dataset and uses those for\n    % initialization of unlabeled frames.\n\n        if nargin < 1 || isempty(modelPath)\n            modelPath = uibrowse([],[],'Select model folder...', 'dir');\n            if isempty(modelPath) || ~exists(modelPath); return; end\n        end\n\n        % TODO: better system for choosing final vs best validation model\n%         if exists(ff(modelPath, 'final_model.h5'))\n%             numValidationSamples = numel(loadvar(ff(modelPath,'training_info.mat'),'val_idx'));\n% %             numWeights = numel(dir_files(ff(modelPath,'weights')));\n%             if numValidationSamples < 500\n%                 modelPath = ff(modelPath,'final_model.h5');\n%             end\n%         end\n        numValidationSamples = numel(loadvar(ff(modelPath,'training_info.mat'),'val_idx'));\n        if exists(ff(modelPath, 'best_model.h5')) && numValidationSamples > 500\n            modelPath = ff(modelPath, 'best_model.h5');\n        else\n            modelPath = ff(modelPath, 'final_model.h5');\n        end\n\n        % Predict\n        preds = predict_box(boxPath, modelPath, false);\n\n        % Save\n        labels.initialization = preds.positions_pred;\n        saveLabels();\n\n        % Update status\n        isInitialized = squeeze(all(~isnan(labels.initialization),2));\n        numInitialized = sum(all(isInitialized,1));\n        ui.status.framesInitialized.String = sprintf('Initialized: %d/%d (%.2f%%)', numInitialized, numFrames, numInitialized/numFrames*100);\n\n        % Update status bars\n        status = getStatus();\n        ui.status.fullImg.CData = status;\n        zoom_idx = ui.status.zoomImg.XData > 0 & ui.status.zoomImg.XData <= size(status,2);\n        ui.status.zoomImg.CData(:,zoom_idx) = status(:,ui.status.zoomImg.XData(zoom_idx));\n\n        % Log event\n        addToHistory(['Initialized with model: ' modelPath])\n\n        % Calculate error rate on labels\n        labeled = all(getStatus() == 2,1);\n        pos_gt = labels.positions(:,:,labeled);\n        pos_pred = labels.initialization(:,:,labeled);\n        pred_metrics = compute_errors(pos_pred,pos_gt);\n\n        % Display errors\n        printf('Error: mean = %.2f, s.d. = %.2f', mean(pred_metrics.euclidean(:)), std(pred_metrics.euclidean(:)))\n        prcs = [50 75 90];\n        prc_errs = prctile(pred_metrics.euclidean(:), prcs);\n        for i = 1:numel(prcs)\n            printf('       %d%% = %.3f', prcs(i), prc_errs(i))\n        end\n\n        % Replot\n        goToFrame(ui.status.currentFrame);\n    end\n    function generateTrainingSet()\n        \n        % Default save path\n        defaultSavePath = ff(fileparts(boxPath), 'training', [get_filename(boxPath,true) '.h5']);\n        defaultSavePath = get_new_filename(defaultSavePath,true);\n        \n        % Create dialog with parameters\n        [params, buttonPressed] = settingsdlg(...\n            'WindowWidth', 400,...\n            'title','Generate a training set', ...\n            'Description','Export a dataset for training based on the current labels.',...\n            'separator','General options',...\n            {'Save path';'savePath'}, defaultSavePath, ...\n            {'Scale - for resizing images';'scale'}, 1, ...\n            {'Sigma - kernel size for confidence maps';'sigma'}, 5, ...\n            {'Test set fraction - held out frames';'testFraction'},0.1,...\n            {'Shuffle - randomize saved dataset order';'postShuffle'}, true, ...\n            {'Compress - reduce file size, but slower to load';'compress'}, true, ...\n            'separator','Data mirroring',...\n            {'Mirror images - augment by flipping along the body axis';'mirroring'}, [true, false], ...\n            {'Animal orientation';'animalOrientation'},  {'left/right','top/bottom'} ...\n            );\n        \n        % Cancel if OK was not pressed (cancel or window closed)\n        if ~strcmpi(buttonPressed,'ok'); return; end\n        \n        % Convert listbox input to boolean for orientation\n        params.horizontalOrientation = strcmpi(params.animalOrientation,'left/right');\n        \n        % Check for existing save path\n        if exists(params.savePath)\n            answer = questdlg('Save path already exists, overwrite existing file?', 'Overwrite file', 'Overwrite', 'Cancel', 'Overwrite');\n            if ~strcmpi(answer, 'Overwrite'); return; end\n        end\n        \n        % Run!\n        generate_training_set(boxPath,params);\n        \n        % Log action\n        addToHistory('Generated training set.')\n    end\n    function fastTrain()\n        % Generate a training set for fast training from current labels\n        \n        % Build default output path\n        runName = sprintf('%s-n=%d', datestr(now,'yymmdd_HHMMSS'), numLabeled);\n        defaultModelsFolder = ff(fileparts(boxPath), 'models');\n        \n        % Create dialog with parameters\n        [params, buttonPressed] = settingsdlg(...\n            'WindowWidth', 500,...\n            'title','Fast training', ...\n            'Description','Quickly train a model using current labels and predict on remaining frames as initialization.',...\n            'separator','Dataset',...\n            {'Scale - for resizing images';'scale'}, 1, ...\n            {'Sigma - kernel size for confidence maps';'sigma'}, 5, ...\n            'separator','Data mirroring',...\n            {'Mirror images - augment by flipping along the body axis';'mirroring'}, [true, false], ...\n            {'Animal orientation';'animalOrientation'}, {'left/right','top/bottom'}, ...\n            'separator','Model',...\n            {'Network architecture';'netName'},{'leap_cnn','hourglass','stacked_hourglass'},...\n            {'Filters - base number of filters for model';'filters'},32,...\n            {'Upsampling layers - use bilinear upsampling instead of transposed conv';'upsamplingLayers'},true,...\n            'separator','Training',...\n            {'Model path - folder to save run data to';'modelsFolder'},defaultModelsFolder,...\n            {'Rotate angle - augment data via random rotations';'rotateAngle'},5,...\n            {'Validation set fraction - frames used for validation';'valSize'},0.1,...\n            {'Epochs - number of rounds of training';'epochs'},15,...\n            {'Batch size - number of samples per batch';'batchSize'},50,...\n            {'Batches per epoch - number of batches of samples per round';'batchesPerEpoch'},50,...\n            {'Validation batches per epoch - number of batches to use for validation';'valBatchesPerEpoch'},10,...\n            {'Save every epoch - save weights from every epoch instead of just best+final';'saveEveryEpoch'},false,...\n            'separator','Training (advanced)',...\n            {'Reduce LR factor - drop learning rate when loss plateaus';'reduceLRFactor'},0.1,...\n            {'Reduce LR patience - wait after loss plateaus before reducing LR';'reduceLRPatience'},2,...\n            {'Reduce LR cooldown - wait after reducing LR before detecting plateau';'reduceLRCooldown'},0,...\n            {'Reduce LR min delta - minimum change in loss to not plateau';'reduceLRMinDelta'},1e-5,...\n            {'Reduce LR min LR - minimum LR to not drop below';'reduceLRMinLR'},1e-10,...\n            {'AMSGrad - optimizer variant for more emphasis on rare data';'amsgrad'},true ...\n            );\n        \n        % Cancel if OK was not pressed (cancel or window closed)\n        if ~strcmpi(buttonPressed,'ok'); return; end\n        \n        % Convert listbox input to boolean for orientation\n        params.horizontalOrientation = strcmpi(params.animalOrientation,'left/right');\n        \n        % Generate temporary training set file\n        dataPath = [tempname '.h5'];\n        dataPath = generate_training_set(boxPath,'savePath',dataPath,...\n            'scale',params.scale,...\n            'mirroring',params.mirroring,...\n            'horizontalOrientation',params.horizontalOrientation,...\n            'sigma',params.sigma, ...\n            'normalizeConfmaps',true,...\n            'postShuffle',true, ...\n            'testFraction',0);\n        \n        % Log action\n        addToHistory(sprintf('Fast training (n = %d)', numLabeled))\n        \n        % Create CLI command for training\n        basePath = fileparts(funpath(true));\n        cmd = {\n            'python'\n            ['\"' ff(basePath, 'training.py') '\"']\n            ['\"' dataPath '\"']\n            ['--base-output-path=\"' params.modelsFolder '\"']\n            ['--run-name=\"' runName '\"']\n            ['--net-name=\"' params.netName '\"']\n            sprintf('--filters=%d',params.filters)\n            sprintf('--rotate-angle=%d', params.rotateAngle)\n            sprintf('--val-size=%.5f', params.valSize)\n            sprintf('--epochs=%d', params.epochs)\n            sprintf('--batch-size=%d', params.batchSize)\n            sprintf('--batches-per-epoch=%d', params.batchesPerEpoch)\n            sprintf('--val-batches-per-epoch=%d', params.valBatchesPerEpoch)\n            sprintf('--reduce-lr-factor=%.10f', params.reduceLRFactor)\n            sprintf('--reduce-lr-patience=%d', params.reduceLRPatience)\n            sprintf('--reduce-lr-cooldown=%d', params.reduceLRCooldown)\n            sprintf('--reduce-lr-min-delta=%.10f', params.reduceLRMinDelta)\n            sprintf('--reduce-lr-min-lr=%.10f', params.reduceLRMinLR)\n            };\n        \n        if params.upsamplingLayers; cmd{end+1} = '--upsampling-layers'; end\n        if params.saveEveryEpoch; cmd{end+1} = '--save-every-epoch'; end\n        if params.amsgrad; cmd{end+1} = '--amsgrad'; end\n        \n        cmd = strjoin(cmd);\n        disp(cmd)\n\n        % Train!\n        try\n            exit_code = system(cmd);\n%             [exit_code,cmd_output] = system(cmd);\n        catch ME\n            delete(dataPath)\n            rethrow(ME)\n        end\n        delete(dataPath)\n\n        % TODO: parse this out from python output?\n        modelPath = ff(params.modelsFolder, runName);\n\n        % Run trained model on data to initialize labels\n        if exists(ff(modelPath, 'final_model.h5'))\n            predictInitializations(modelPath)\n        end\n    end\n\n%% Ploting\ninitializeSkeleton();\n\n    function initializeSkeleton()\n    % Creates graphics objects representing the interactive skeleton\n\n        % Draw each line segment\n        if ~isempty(ui.skel.segs); delete(ui.skel.segs); end\n        ui.skel.segs = gobjects(size(labels.skeleton.segments,1),1);\n        for i = 1:numel(ui.skel.segs)\n            % Find default position of each nodes in the segment\n            pos = labels.skeleton.pos(labels.skeleton.segments.joints_idx{i},:);\n\n            % Plot\n            ui.skel.segs(i) = plot(ui.img.ax, pos(:,1), pos(:,2), '.-', ...\n                'Color',labels.skeleton.segments.color{i});\n\n            % Add metadata\n            ui.skel.segs(i).UserData.seg_idx = i;\n            ui.skel.segs(i).UserData.seg_joints_idx = labels.skeleton.segments.joints_idx{i};\n        end\n\n        % Clicks on the skeleton edges should pass through to the image\n        set(ui.skel.segs, 'PickableParts', 'none');\n\n        % Draw each joint node\n        if ~isempty(ui.skel.nodes); delete(ui.skel.nodes); end\n%         status = getStatus(ui.status.currentFrame); statusCmap(status(i)+1,:)\n        ui.skel.nodes = gobjects(height(labels.skeleton.joints),1);\n        for i = 1:numel(ui.skel.nodes)\n            ui.skel.nodes(i) = plot(ui.img.ax,labels.skeleton.joints.pos(i,1),labels.skeleton.joints.pos(i,2),'o',...\n                'Color','w', 'LineWidth', 1, 'PickableParts','none');\n            ui.skel.nodes(i).UserData.node_idx = i;\n        end\n\n        % Make movable and add callbacks\n        if config.draggable\n            set(ui.skel.nodes, 'PickableParts','visible');\n            draggable(ui.skel.nodes, @nodesMoved, 'endFcn', @nodesMoveEnd);\n        end\n    end\n    function pos = getNodePositions()\n    % Utility function that returns node positions from the corresponding graphics objects\n        pos = NaN(numel(ui.skel.nodes),2);\n        for i = 1:numel(ui.skel.nodes)\n            pos(i,:) = [ui.skel.nodes(i).XData ui.skel.nodes(i).YData];\n        end\n    end\n    function updateSkeleton(pos)\n    % Updates pre-initialiazed skeleton graphics objects\n\n        if nargin < 1\n            % Get current node positions from graphics\n            pos = getNodePositions();\n        else\n            % Update node positions\n            for i = 1:size(pos,1)\n                % Check for modification to graphics positions\n                old_pos = [ui.skel.nodes(i).XData ui.skel.nodes(i).YData];\n                if ~isequal(pos(i,:), old_pos)\n                    % Update graphics\n                    ui.skel.nodes(i).XData = pos(i,1);\n                    ui.skel.nodes(i).YData = pos(i,2);\n                end\n            end\n        end\n\n        % Check for changes\n        for i = 1:numNodes\n            if ~isequal(pos(i,:), ui.status.initialPos(i,:))\n                % Mark node as moved\n                ui.status.movedNodes(i) = true;\n\n                % Denote unsaved changes\n                ui.status.unsavedChanges(ui.status.currentFrame) = true;\n            end\n        end\n\n        % Set defaults\n        set(ui.skel.nodes, 'Marker', 'o'); % Default marker (no changes)\n        set(ui.skel.nodes, 'MarkerSize', config.nodeSize); % Default size (unselected)\n        set(ui.ctrl.refNodes, 'MarkerSize', config.nodeSize); % Default size (unselected)\n\n        % Update node colors based on status\n        status = getStatus(ui.status.currentFrame);\n        for i = 1:numNodes\n            % Set status color\n            ui.skel.nodes(i).Color = statusCmap(status(i)+1,:);\n\n            % Uncommitted changes\n            if ui.status.movedNodes(i); ui.skel.nodes(i).Marker = 's'; end\n\n            % Selected node\n            if ui.status.selectedNode == i\n                ui.skel.nodes(i).MarkerSize = 9;\n                ui.ctrl.refNodes(i).MarkerSize = 9;\n            end\n        end\n\n        % Update edges\n        for i = 1:numel(ui.skel.segs)\n            ui.skel.segs(i).XData(:) = pos(ui.skel.segs(i).UserData.seg_joints_idx,1);\n            ui.skel.segs(i).YData(:) = pos(ui.skel.segs(i).UserData.seg_joints_idx,2);\n        end\n\n        drawnow;\n    end\n\n    function nodesMoved(h)\n    % Called while node is being moved to update skeleton\n\n        % Get node index\n        node_idx = h.UserData.node_idx;\n\n        % Set selected node\n        if ui.status.selectedNode ~= node_idx\n            selectNode(node_idx)\n        end\n\n        % Update\n        updateSkeleton()\n\n    end\n\n    function nodesMoveEnd(h)\n    % Called when the node is released after moving\n        % Get node index\n        node_idx = h.UserData.node_idx;\n\n        % Set selected node\n        if ui.status.selectedNode ~= node_idx\n            selectNode(node_idx)\n        end\n\n        % Update\n        updateSkeleton()\n\n    end\n\n    function selectNode(i)\n    % Utility function that sets the selected node across the entire GUI\n\n        % Check for changes\n        previousSelection = ui.status.selectedNode;\n\n        if ~isequal(previousSelection, i)\n            % Set selected node\n            ui.status.selectedNode = i;\n\n            % Update listbox\n            ui.ctrl.jointsList.Value = i;\n\n            % Update graphics\n            updateSkeleton();\n        end\n    end\n\n    function nudgeNode(dXY, i)\n    % Utility function for moving a node by a delta amount\n        if nargin < 2; i = ui.status.selectedNode; end\n\n        % Get and update node position\n        pos = getNodePositions();\n        pos(i,:) = pos(i,:) + dXY;\n\n        % Update\n        updateSkeleton(pos);\n    end\n\n    function nudgeSegment(dXY, i)\n    % Utility function for moving all segments with a node by a delta amount\n        if nargin < 2; i = ui.status.selectedNode; end\n\n        % Find each segment with the current node and pull out all nodes\n        seg_nodes = {};\n        for j = 1:height(labels.skeleton.segments)\n            idx = labels.skeleton.segments.joints_idx{j};\n            if any(idx == i)\n                seg_nodes{end+1} = idx;\n            end\n        end\n\n        % Get the union of the set to make sure we don't double move any nodes\n        seg_nodes = unique(cellcat(seg_nodes));\n\n        % Get current positions\n        pos = getNodePositions();\n\n        % Move all nodes\n        for j = 1:numel(seg_nodes)\n            pos(j,:) = pos(j,:) + dXY;\n        end\n\n        % Update edges\n         updateSkeleton(pos);\n    end\n\n    function setNodesToDefault(node_idx)\n    % Utility function to reset nodes to default position from the skeleton template\n        if nargin < 1; node_idx = 1:numNodes; end\n\n        % Get current positions\n        pos = getNodePositions();\n\n        % Get default positions\n        default_pos = labels.skeleton.joints.pos;\n\n        % Update with defaults\n%         pos(node_idx,:) = default_pos(node_idx,:);\n        labels.positions(node_idx,:,ui.status.currentFrame) = NaN;\n        ui.status.movedNodes(node_idx) = false;\n\n        % Update\n        updateSkeleton();\n    end\n\n    function pos = getInitialPos(idx)\n    % Utility to compute the initial node positions for a single frame\n        if nargin < 1; idx = ui.status.currentFrame; end\n\n        % Start off with defaults\n        pos = labels.skeleton.joints.pos;\n\n        % Update with initialized positions\n        init_pos = labels.initialization(:,:,idx);\n        init_nodes = find(all(~isnan(init_pos),2));\n        pos(init_nodes,:) = init_pos(init_nodes,:);\n\n        % Update with user-labeled positions\n        label_pos = labels.positions(:,:,idx);\n        label_nodes = find(all(~isnan(label_pos),2));\n        pos(label_nodes,:) = label_pos(label_nodes,:);\n    end\n\n    function resetNodes(node_idx)\n    % Utility function to reset nodes to their initial positions when the frame was drawn\n        if nargin < 1; node_idx = 1:numNodes; end\n\n        % Start off with what we have now\n        pos = getNodePositions();\n\n        % Get initial postions\n        init_pos = getInitialPos(ui.status.currentFrame);\n\n        % Set positions for specified nodes\n        pos(node_idx,:) = init_pos(node_idx);\n\n        % Update\n        updateSkeleton(pos);\n    end\n\n%% Frame update and saving\n    function markAllCorrect()\n    % Helper for setting all nodes in the current frame as correct\n        ui.status.movedNodes(:) = true;\n        commitChanges();\n    end\n    function commitChanges()\n    % Utility function for committing changes to node positions in the\n    % current frame to the labels structure (but does not save to disk)\n\n        % Get current positions\n        pos = getNodePositions();\n\n        % Check current status\n        status = getStatus(ui.status.currentFrame);\n        isLabeled = all(status == 2);\n\n        for i = horz(find(ui.status.movedNodes))\n            % Commit to labels\n            labels.positions(i,:,ui.status.currentFrame) = pos(i,:);\n\n            % Reset moved state\n            ui.status.movedNodes(i) = false;\n\n            % Mark unsaved changes\n            ui.status.unsavedChanges(ui.status.currentFrame) = true;\n        end\n\n        % Update status\n        status = getStatus(ui.status.currentFrame);\n\n        % Update skeleton display\n        %updateSkeleton();\n\n        % Update stats if changed\n        if ~all(isLabeled) && all(status == 2)\n            addToHistory(sprintf('Labeled frame %d', ui.status.currentFrame));\n            numLabeled = numLabeled + 1;\n            ui.status.framesLabeled.String = sprintf('Labeled: %d/%d (%.2f%%)', numLabeled, numFrames, numLabeled/numFrames*100);\n        end\n\n        % Update full status data\n        ui.status.fullImg.CData(:,ui.status.currentFrame) = status;\n\n        % Update zoomed status bar data\n        zoomBoxIdx = ui.status.zoomImg.XData;\n        if any(zoomBoxIdx == ui.status.currentFrame)\n            ui.status.zoomImg.CData(:,zoomBoxIdx == ui.status.currentFrame) = status;\n        end\n\n        % Update status fig title\n        savedStatus = '';\n        if any(ui.status.unsavedChanges); savedStatus = ' [unsaved]'; end\n        ui.status.fig.Name = sprintf('Status: %d/%d (%.2f%%) labeled%s', numLabeled, numFrames, numLabeled/numFrames*100, savedStatus);\n    end\n    function goToFrame(idx)\n    % Utility function for seeking to another frame\n\n        % Commit changes to labels\n        commitChanges();\n\n        % Autosave before anything\n        if config.autoSave && ~isempty(ui.status.currentFrame) && ui.status.unsavedChanges(ui.status.currentFrame)\n            saveLabels();\n        end\n\n        % Update image\n        ui.img.img.CData = box(:,:,:,idx);\n\n        % Update status\n        ui.status.currentFrame = idx;\n        ui.img.fig.Name = sprintf('%d/%d', ui.status.currentFrame, numFrames);\n\n        % Get initial positions\n        ui.status.initialPos = getInitialPos(idx);\n\n        % Update with initial positions\n        updateSkeleton(ui.status.initialPos);\n\n        % Update status zoom box position\n        zoomBoxIdx = zoomBoxWindow + ui.status.currentFrame;\n        zoomBoxPts = [\n            zoomBoxIdx(1) 0\n            zoomBoxIdx(end) 0\n            zoomBoxIdx(end) numNodes\n            zoomBoxIdx(1) numNodes\n            zoomBoxIdx(1) 0\n            ];\n        ui.status.fullZoomBox.XData = zoomBoxPts(:,1);\n        ui.status.fullZoomBox.YData = zoomBoxPts(:,2);\n\n        % Update zoomed status bar data\n        ui.status.zoomImg.XData = zoomBoxIdx;\n        ui.status.zoomImg.CData(:) = 0; % reset\n        isValidIdx = zoomBoxIdx > 0 & zoomBoxIdx <= numFrames;\n        ui.status.zoomImg.CData(:,isValidIdx) = ui.status.fullImg.CData(:,zoomBoxIdx(isValidIdx));\n        ui.status.zoomAx.XLim = zoomBoxIdx([1 end]) + [-0.5 0.5];\n    end\n\n    function saveLabels()\n    % Saves everything in the labels structure to disk\n\n        stic;\n        % Commit unsaved changes to labels\n        commitChanges();\n\n        % Update if there were any changes\n        if any(ui.status.unsavedChanges)\n\n            % Update last modified timestamp\n            labels.lastModified = datestr(now);\n        end\n\n        % Save current frame so we pick up where we left off\n        config.initialFrame = ui.status.currentFrame;\n\n        % Save figure positions\n        config.imgFigPos = ui.img.fig.Position;\n        config.ctrlFigPos = ui.ctrl.fig.Position;\n        config.statusFigPos = ui.status.fig.Position;\n\n\n        % Save config\n        labels.config = config;\n\n        % Save to labels file\n        save(labels.savePath, '-struct', 'labels')\n\n        % Clear modified flags\n        ui.status.unsavedChanges(:) = false;\n        commitChanges();\n\n        stocf('Saved labels: %s', labels.savePath)\n    end\n\n\n%% Start!\ngoToFrame(config.initialFrame);\nselectNode(1);\nupdateSkeleton();\n\nend\n"
  },
  {
    "path": "leap/hpc/python_gpu.sh",
    "content": "#!/bin/bash\n#SBATCH --time=4:00:00\n#SBATCH --mem=128000\n#SBATCH -N 1\n#SBATCH --cpus-per-task=4\n#SBATCH --ntasks-per-node=1\n#SBATCH --ntasks-per-socket=1\n#SBATCH --gres=gpu:1\n\necho \"args: ${@:1}\"\n\npython ${@:1}\n"
  },
  {
    "path": "leap/image_augmentation.py",
    "content": "import cv2\nimport numpy as np\nimport keras\nfrom keras.utils import Sequence\n\ndef transform_imgs(X, theta=(-180,180), scale=1.0):\n    \"\"\" Transforms sets of images with the same random transformation across each channel. \"\"\"\n    \n    # Sample random rotation and scale if range specified\n    if not np.isscalar(theta):\n        theta = np.ptp(theta) * np.random.rand() + np.min(theta)\n    if not np.isscalar(scale):\n        scale = np.ptp(scale) * np.random.rand() + np.min(scale)\n    \n    # Standardize X input to a list\n    single_img = type(X) == np.ndarray\n    if single_img:\n        X = [X,]\n    \n    # Find image parameters\n    img_size = X[0].shape[:2]\n    ctr = (img_size[0] / 2, img_size[0] / 2)\n    \n    # Compute affine transformation matrix\n    T = cv2.getRotationMatrix2D(ctr, theta, scale)\n    \n    # Make sure we don't overwrite the inputs\n    X = [x.copy() for x in X]\n    \n    # Apply to each image\n    for i in range(len(X)):\n        if X[i].ndim == 2:\n            # Single channel image\n            X[i] = cv2.warpAffine(X[i], T, img_size[::-1])\n        else:\n            # Multi-channel image\n            for c in range(X[i].shape[-1]):\n                X[i][...,c] = cv2.warpAffine(X[i][...,c], T, img_size[::-1])\n    \n    # Pull the single image back out of the list\n    if single_img:\n        X = X[0]\n    \n    return X\n\n\nclass PairedImageAugmenter(Sequence):\n    def __init__(self, X, Y, batch_size=32, shuffle=False, theta=(-180,180), scale=1.0):\n        self.X = X\n        self.Y = Y\n        self.batch_size = batch_size\n        self.theta = theta\n        self.scale = scale\n        \n        self.num_samples = len(X)\n        all_idx = np.arange(self.num_samples)\n        if shuffle:\n            np.random.shuffle(all_idx)\n        \n        self.batches = np.array_split(all_idx, np.ceil(self.num_samples / self.batch_size))\n        \n    def __len__(self):\n        return len(self.batches)\n    \n    def __getitem__(self, batch_idx):\n        idx = self.batches[batch_idx]\n        X = self.X[idx]\n        Y = self.Y[idx]\n        \n        for i in range(len(X)):\n            X[i], Y[i] = transform_imgs((X[i],Y[i]), theta=self.theta, scale=self.scale)\n        return X, Y\n\n    \nclass MultiInputOutputPairedImageAugmenter(PairedImageAugmenter):\n    def __init__(self, input_names, output_names, *args, **kwargs):\n        if type(input_names) != list:\n            input_names = [input_names,]\n        if type(output_names) != list:\n            output_names = [output_names,]\n        self.input_names = input_names\n        self.output_names = output_names\n        super().__init__(*args, **kwargs)\n        \n    def __getitem__(self, batch_idx):\n        X,Y = super().__getitem__(batch_idx)\n        return ({k: X for k in self.input_names}, {k: Y for k in self.output_names})\n    "
  },
  {
    "path": "leap/layers.py",
    "content": "# -*- coding: utf-8 -*-\n\"\"\"\n   Copyright 2018 Jacob M. Graving <jgraving@gmail.com>\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n   Modified from code written by François Chollet \n   All contributions by François Chollet:\n   Copyright (c) 2015 - 2018, François Chollet.\n   All rights reserved.\n\"\"\"\n\nimport numpy as np\n\nimport keras\nimport keras.backend as K\nfrom keras.legacy import interfaces\nfrom keras.engine import Layer\nfrom keras.engine import InputSpec\n\nfrom keras.utils import conv_utils\nfrom keras.backend import int_shape, permute_dimensions\n\nfrom keras.backend import tf\n\nfrom keras.layers import Conv2D, Add\n\nfrom packaging.version import parse as parse_version\n\n__all__ = ['UpSampling2D', 'Maxima2D']\n\n\ndef resize_images(x, height_factor, width_factor, interpolation, data_format):\n    \"\"\"Resizes the images contained in a 4D tensor.\n    # Arguments\n        x: Tensor or variable to resize.\n        height_factor: Positive integer.\n        width_factor: Positive integer.\n        interpolation: string, \"nearest\", \"bilinear\" or \"bicubic\"\n        data_format: string, `\"channels_last\"` or `\"channels_first\"`.\n    # Returns\n        A tensor.\n    # Raises\n        ValueError: if `data_format` is neither `\"channels_last\"` or `\"channels_first\"`.\n    \"\"\"\n    if interpolation == 'nearest':\n        tf_resize = tf.image.resize_nearest_neighbor\n    elif interpolation == 'bilinear':\n        tf_resize = tf.image.resize_bilinear\n    elif interpolation == 'bicubic':\n        tf_resize = tf.image.resize_bicubic\n    else:\n        raise ValueError('Invalid interpolation method:', interpolation)\n    if data_format == 'channels_first':\n        original_shape = int_shape(x)\n        new_shape = tf.shape(x)[2:]\n        new_shape *= tf.constant(np.array([height_factor, width_factor]).astype('int32'))\n        x = permute_dimensions(x, [0, 2, 3, 1])\n        x = tf_resize(x, new_shape, align_corners=True)\n        x = permute_dimensions(x, [0, 3, 1, 2])\n        x.set_shape((None, None, original_shape[2] * height_factor if original_shape[2] is not None else None,\n                     original_shape[3] * width_factor if original_shape[3] is not None else None))\n        return x\n    elif data_format == 'channels_last':\n        original_shape = int_shape(x)\n        new_shape = tf.shape(x)[1:3]\n        new_shape *= tf.constant(np.array([height_factor, width_factor]).astype('int32'))\n        x = tf_resize(x, new_shape, align_corners=True)\n        x.set_shape((None, original_shape[1] * height_factor if original_shape[1] is not None else None,\n                     original_shape[2] * width_factor if original_shape[2] is not None else None, None))\n        return x\n    else:\n        raise ValueError('Invalid data_format:', data_format)\n\n\nclass UpSampling2D(Layer):\n    \"\"\"Upsampling layer for 2D inputs.\n    Repeats the rows and columns of the data\n    by size[0] and size[1] respectively with bilinear interpolation.\n    # Arguments\n        size: int, or tuple of 2 integers.\n            The upsampling factors for rows and columns.\n        data_format: A string,\n            one of `channels_last` (default) or `channels_first`.\n            The ordering of the dimensions in the inputs.\n            `channels_last` corresponds to inputs with shape\n            `(batch, height, width, channels)` while `channels_first`\n            corresponds to inputs with shape\n            `(batch, channels, height, width)`.\n            It defaults to the `image_data_format` value found in your\n            Keras config file at `~/.keras/keras.json`.\n            If you never set it, then it will be \"channels_last\".\n        interpolation: A string,\n            one of 'nearest' (default), 'bilinear', or 'bicubic'\n    # Input shape\n        4D tensor with shape:\n        - If `data_format` is `\"channels_last\"`:\n            `(batch, rows, cols, channels)`\n        - If `data_format` is `\"channels_first\"`:\n            `(batch, channels, rows, cols)`\n    # Output shape\n        4D tensor with shape:\n        - If `data_format` is `\"channels_last\"`:\n            `(batch, upsampled_rows, upsampled_cols, channels)`\n        - If `data_format` is `\"channels_first\"`:\n            `(batch, channels, upsampled_rows, upsampled_cols)`\n    \"\"\"\n\n    @interfaces.legacy_upsampling2d_support\n    def __init__(self, size=(2, 2), data_format=None, interpolation='nearest', **kwargs):\n        super(UpSampling2D, self).__init__(**kwargs)\n        # Update to K.normalize_data_format after keras 2.2.0\n        if parse_version(keras.__version__) > parse_version(\"2.2.0\"):\n            self.data_format = K.normalize_data_format(data_format)\n        else:\n            self.data_format = conv_utils.normalize_data_format(data_format)\n\n        self.interpolation = interpolation\n        self.size = conv_utils.normalize_tuple(size, 2, 'size')\n        self.input_spec = InputSpec(ndim=4)\n\n    def compute_output_shape(self, input_shape):\n        if self.data_format == 'channels_first':\n            height = self.size[0] * input_shape[2] if input_shape[2] is not None else None\n            width = self.size[1] * input_shape[3] if input_shape[3] is not None else None\n            return (input_shape[0],\n                    input_shape[1],\n                    height,\n                    width)\n        elif self.data_format == 'channels_last':\n            height = self.size[0] * input_shape[1] if input_shape[1] is not None else None\n            width = self.size[1] * input_shape[2] if input_shape[2] is not None else None\n            return (input_shape[0],\n                    height,\n                    width,\n                    input_shape[3])\n\n    def call(self, inputs):\n        return resize_images(inputs, self.size[0], self.size[1],\n                             self.interpolation, self.data_format)\n\n    def get_config(self):\n        config = {'size': self.size,\n                  'data_format': self.data_format}\n        base_config = super(UpSampling2D, self).get_config()\n        return dict(list(base_config.items()) + list(config.items()))\n\n\ndef _find_maxima(x):\n\n    x = K.cast(x, K.floatx())\n\n    col_max = K.max(x, axis=1)\n    row_max = K.max(x, axis=2)\n\n    maxima = K.max(col_max, 1)\n    maxima = K.expand_dims(maxima, -2)\n\n    cols = K.cast(K.argmax(col_max, -2), K.floatx())\n    rows = K.cast(K.argmax(row_max, -2), K.floatx())\n    cols = K.expand_dims(cols, -2)\n    rows = K.expand_dims(rows, -2)\n\n    # maxima = K.concatenate([rows, cols, maxima], -2) # y, x, val\n    maxima = K.concatenate([cols, rows, maxima], -2) # x, y, val\n\n    return maxima\n\n\ndef find_maxima(x, data_format):\n    \"\"\"Finds the 2D maxima contained in a 4D tensor.\n    # Arguments\n        x: Tensor or variable.\n        data_format: string, `\"channels_last\"` or `\"channels_first\"`.\n    # Returns\n        A tensor.\n    # Raises\n        ValueError: if `data_format` is neither `\"channels_last\"` or `\"channels_first\"`.\n    \"\"\"\n    if data_format == 'channels_first':\n        x = permute_dimensions(x, [0, 2, 3, 1])\n        x = _find_maxima(x)\n        x = permute_dimensions(x, [0, 2, 1])\n        return x\n    elif data_format == 'channels_last':\n        x = _find_maxima(x)\n        return x\n    else:\n        raise ValueError('Invalid data_format:', data_format)\n\n\nclass Maxima2D(Layer):\n    \"\"\"Maxima layer for 2D inputs.\n    Finds the maxima and 2D indices\n    for the channels in the input.\n    The output is ordered as [row, col, maximum].\n    # Arguments\n        data_format: A string,\n            one of `channels_last` (default) or `channels_first`.\n            The ordering of the dimensions in the inputs.\n            `channels_last` corresponds to inputs with shape\n            `(batch, height, width, channels)` while `channels_first`\n            corresponds to inputs with shape\n            `(batch, channels, height, width)`.\n            It defaults to the `image_data_format` value found in your\n            Keras config file at `~/.keras/keras.json`.\n            If you never set it, then it will be \"channels_last\".\n    # Input shape\n        4D tensor with shape:\n        - If `data_format` is `\"channels_last\"`:\n            `(batch, rows, cols, channels)`\n        - If `data_format` is `\"channels_first\"`:\n            `(batch, channels, rows, cols)`\n    # Output shape\n        3D tensor with shape:\n        - If `data_format` is `\"channels_last\"`:\n            `(batch, 3, channels)`\n        - If `data_format` is `\"channels_first\"`:\n            `(batch, channels, 3)`\n    \"\"\"\n\n    def __init__(self, data_format=None, **kwargs):\n        super(Maxima2D, self).__init__(**kwargs)\n        # Update to K.normalize_data_format after keras 2.2.0\n        if parse_version(keras.__version__) > parse_version(\"2.2.0\"):\n            self.data_format = K.normalize_data_format(data_format)\n        else:\n            self.data_format = conv_utils.normalize_data_format(data_format)\n        self.input_spec = InputSpec(ndim=4)\n\n    def compute_output_shape(self, input_shape):\n        if self.data_format == 'channels_first':\n            return (input_shape[0],\n                    input_shape[1],\n                    3)\n        elif self.data_format == 'channels_last':\n            return (input_shape[0],\n                    3,\n                    input_shape[3])\n\n    def call(self, inputs):\n        return find_maxima(inputs, self.data_format)\n\n    def get_config(self):\n        config = {'data_format': self.data_format}\n        base_config = super(Maxima2D, self).get_config()\n        return dict(list(base_config.items()) + list(config.items()))\n\n\ndef residual_bottleneck_module(x_in, output_filters=32, bottleneck_factor=2, prefix=\"res\", activation=\"relu\", initializer=\"glorot_normal\"):\n    # Get input shape and channels\n    in_shape = K.int_shape(x_in)\n    input_filters = in_shape[3]\n    \n    # Bottleneck filters are proportional to the output filters\n    bottleneck_filters = output_filters // bottleneck_factor\n    \n    # Bottleneck block\n    x = Conv2D(filters=bottleneck_filters, kernel_size=1, padding=\"same\", activation=activation, kernel_initializer=initializer, name=prefix + \"_Conv1\")(x_in)\n    x = Conv2D(filters=bottleneck_filters, kernel_size=3, padding=\"same\", activation=activation, kernel_initializer=initializer, name=prefix + \"_Conv2\")(x)\n    x = Conv2D(filters=output_filters, kernel_size=1, padding=\"same\", activation=activation, kernel_initializer=initializer, name=prefix + \"_Conv3\")(x)\n    \n    # 1x1 conv if input channels are different from output channels\n    if output_filters != input_filters:\n        x_in = Conv2D(filters=output_filters, kernel_size=1, padding=\"same\", activation=activation, kernel_initializer=initializer, name=prefix + \"_ConvSkip\")(x_in)\n    \n    # Residual connection\n    x = Add(name=prefix + \"_AddRes\")([x_in, x])\n    \n    return x"
  },
  {
    "path": "leap/models.py",
    "content": "import keras\nfrom keras import backend as K\nfrom keras.models import Model\nfrom keras.layers import Input, Conv2D, Conv2DTranspose, Add, MaxPooling2D\nfrom keras.optimizers import Adam\n\nfrom leap.layers import residual_bottleneck_module, UpSampling2D\n\ndef leap_cnn(img_size, output_channels, filters=64, upsampling_layers=False, amsgrad=False, summary=False):\n    \"\"\"\n    Creates and compiles network model.\n\n    :param img_size: shape of a single image, optionally including channels\n    :param output_channels: number of output channels (joints being predicted)\n    :param filters: number of baseline filters to use (more filters will be used in intermediate layers)\n    :param summary: prints network summary after compiling\n    \"\"\"\n    if len(img_size) == 2:\n        img_size = img_size + (1,)\n\n    x_in = Input(img_size)\n\n    x1 = Conv2D(filters, kernel_size=3, padding=\"same\", activation=\"relu\")(x_in)\n    x1 = Conv2D(filters, kernel_size=3, padding=\"same\", activation=\"relu\")(x1)\n    x1 = Conv2D(filters, kernel_size=3, padding=\"same\", activation=\"relu\")(x1)\n    x1_pool = MaxPooling2D(pool_size=2, strides=2, padding=\"same\")(x1)\n\n    x2 = Conv2D(filters*2, kernel_size=3, padding=\"same\", activation=\"relu\")(x1_pool)\n    x2 = Conv2D(filters*2, kernel_size=3, padding=\"same\", activation=\"relu\")(x2)\n    x2 = Conv2D(filters*2, kernel_size=3, padding=\"same\", activation=\"relu\")(x2)\n    x2_pool = MaxPooling2D(pool_size=2, strides=2, padding=\"same\")(x2)\n\n    x3 = Conv2D(filters*4, kernel_size=3, padding=\"same\", activation=\"relu\")(x2_pool)\n    x3 = Conv2D(filters*4, kernel_size=3, padding=\"same\", activation=\"relu\")(x3)\n    x3 = Conv2D(filters*4, kernel_size=3, padding=\"same\", activation=\"relu\")(x3)\n\n    if upsampling_layers:\n        x4 = UpSampling2D(interpolation=\"bilinear\")(x3)\n    else:\n        x4 = Conv2DTranspose(filters*2, kernel_size=3, strides=2, padding=\"same\", activation=\"relu\", kernel_initializer=\"glorot_normal\")(x3)\n    x4 = Conv2D(filters*2, kernel_size=3, padding=\"same\", activation=\"relu\")(x4)\n    x4 = Conv2D(filters*2, kernel_size=3, padding=\"same\", activation=\"relu\")(x4)\n\n    if upsampling_layers:\n        x_out = UpSampling2D(interpolation=\"bilinear\")(x4)\n        x_out = Conv2D(output_channels, kernel_size=3, padding=\"same\", activation=\"linear\")(x_out)\n    else:\n        x_out = Conv2DTranspose(output_channels, kernel_size=3, strides=2, padding=\"same\", activation=\"linear\", kernel_initializer=\"glorot_normal\")(x4)\n\n    # Compile\n    net = Model(inputs=x_in, outputs=x_out, name=\"LeapCNN\")\n    net.compile(optimizer=Adam(amsgrad=amsgrad), loss=\"mean_squared_error\")\n\n    if summary:\n        net.summary()\n\n    return net\n\ndef hourglass(img_size, output_channels, filters=64, upsampling_layers=False, amsgrad=False, summary=False):\n    \"\"\"\n    Creates and compiles network model.\n\n    :param img_size: shape of a single image, optionally including channels\n    :param output_channels: number of output channels (joints being predicted)\n    :param filters: number of baseline filters to use (more filters will be used in intermediate layers)\n    :param summary: prints network summary after compiling\n    \"\"\"\n\n    if len(img_size) == 2:\n        img_size = img_size + (1,)\n\n    x_in = Input(img_size, name=\"x_in\")\n\n    x1_pre = residual_bottleneck_module(x_in, prefix=\"x1\", output_filters=filters)\n    x1 = MaxPooling2D(pool_size=2, strides=2, padding=\"same\", name=\"x1_pool\")(x1_pre)\n\n    x2_pre = residual_bottleneck_module(x1, prefix=\"x2\", output_filters=filters)\n    x2 = MaxPooling2D(pool_size=2, strides=2, padding=\"same\", name=\"x2_pool\")(x2_pre)\n\n    x3_pre = residual_bottleneck_module(x2, prefix=\"x3\", output_filters=filters)\n    x3 = MaxPooling2D(pool_size=2, strides=2, padding=\"same\", name=\"x3_pool\")(x3_pre)\n\n    x4_pre = residual_bottleneck_module(x3, prefix=\"x4\", output_filters=filters)\n    x4 = MaxPooling2D(pool_size=2, strides=2, padding=\"same\", name=\"x4_pool\")(x4_pre)\n\n\n    x5 = residual_bottleneck_module(x4, prefix=\"x5\", output_filters=filters)\n\n\n    if upsampling_layers:\n        x6_pre = UpSampling2D(interpolation=\"bilinear\", name=\"x6_Upsample\")(x5)\n    else:\n        x6_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding=\"same\", activation=\"relu\", kernel_initializer=\"glorot_normal\", name=\"x6_ConvT\")(x5)\n    x6_add = Add(name=\"x6_Add\")([x4_pre, x6_pre])\n    x6 = residual_bottleneck_module(x6_add, prefix=\"x6\", output_filters=filters)\n\n    if upsampling_layers:\n        x7_pre = UpSampling2D(interpolation=\"bilinear\", name=\"x7_Upsample\")(x6)\n    else:\n        x7_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding=\"same\", activation=\"relu\", kernel_initializer=\"glorot_normal\", name=\"x7_ConvT\")(x6)\n    x7_add = Add(name=\"x7_Add\")([x3_pre, x7_pre])\n    x7 = residual_bottleneck_module(x7_add, prefix=\"x7\", output_filters=filters)\n\n    if upsampling_layers:\n        x8_pre = UpSampling2D(interpolation=\"bilinear\", name=\"x8_Upsample\")(x7)\n    else:\n        x8_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding=\"same\", activation=\"relu\", kernel_initializer=\"glorot_normal\", name=\"x8_ConvT\")(x7)\n    x8_add = Add(name=\"x8_Add\")([x2_pre, x8_pre])\n    x8 = residual_bottleneck_module(x8_add, prefix=\"x8\", output_filters=filters)\n\n    if upsampling_layers:\n        x9_pre = UpSampling2D(interpolation=\"bilinear\", name=\"x9_Upsample\")(x8)\n    else:\n        x9_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding=\"same\", activation=\"relu\", kernel_initializer=\"glorot_normal\", name=\"x9_ConvT\")(x8)\n    x9_add = Add(name=\"x9_Add\")([x1_pre, x9_pre])\n    x9 = residual_bottleneck_module(x9_add, prefix=\"x9\", output_filters=filters)\n\n    x_out = Conv2D(filters=output_channels, kernel_size=3, strides=1, padding=\"same\", activation=\"linear\", name=\"x_out\")(x9)\n\n    # Compile\n    model = Model(inputs=x_in, outputs=x_out, name=\"hourglass\")\n    model.compile(optimizer=Adam(amsgrad=amsgrad), loss=\"mean_squared_error\")\n\n    if summary:\n        model.summary()\n\n    return model\n\n\n\ndef stacked_hourglass(img_size, output_channels, filters=64, upsampling_layers=False, amsgrad=False, summary=False):\n    \"\"\"\n    Creates and compiles network model.\n\n    :param img_size: shape of a single image, optionally including channels\n    :param output_channels: number of output channels (joints being predicted)\n    :param filters: number of baseline filters to use (more filters will be used in intermediate layers)\n    :param summary: prints network summary after compiling\n    \"\"\"\n\n    if len(img_size) == 2:\n        img_size = img_size + (1,)\n\n    x_in = Input(img_size, name=\"x_in\")\n\n    x1_1_pre = residual_bottleneck_module(x_in, prefix=\"x1_1\", output_filters=filters)\n    x1_1 = MaxPooling2D(pool_size=2, strides=2, padding=\"same\", name=\"x1_1_pool\")(x1_1_pre)\n\n    x1_2_pre = residual_bottleneck_module(x1_1, prefix=\"x1_2\", output_filters=filters)\n    x1_2 = MaxPooling2D(pool_size=2, strides=2, padding=\"same\", name=\"x1_2_pool\")(x1_2_pre)\n\n    x1_3_pre = residual_bottleneck_module(x1_2, prefix=\"x1_3\", output_filters=filters)\n    x1_3 = MaxPooling2D(pool_size=2, strides=2, padding=\"same\", name=\"x1_3_pool\")(x1_3_pre)\n\n    x1_4_pre = residual_bottleneck_module(x1_3, prefix=\"x1_4\", output_filters=filters)\n    x1_4 = MaxPooling2D(pool_size=2, strides=2, padding=\"same\", name=\"x1_4_pool\")(x1_4_pre)\n\n\n    x1_5 = residual_bottleneck_module(x1_4, prefix=\"x1_5\", output_filters=filters)\n\n\n    if upsampling_layers:\n        x1_6_pre = UpSampling2D(interpolation=\"bilinear\", name=\"x1_6_Upsample\")(x1_5)\n    else:\n        x1_6_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding=\"same\", activation=\"relu\", kernel_initializer=\"glorot_normal\", name=\"x1_6_ConvT\")(x1_5)\n    x1_6_add = Add(name=\"x1_6_Add\")([x1_4_pre, x1_6_pre])\n    x1_6 = residual_bottleneck_module(x1_6_add, prefix=\"x1_6\", output_filters=filters)\n\n    if upsampling_layers:\n        x1_7_pre = UpSampling2D(interpolation=\"bilinear\", name=\"x1_7_Upsample\")(x1_6)\n    else:\n        x1_7_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding=\"same\", activation=\"relu\", kernel_initializer=\"glorot_normal\", name=\"x1_7_ConvT\")(x1_6)\n    x1_7_add = Add(name=\"x1_7_Add\")([x1_3_pre, x1_7_pre])\n    x1_7 = residual_bottleneck_module(x1_7_add, prefix=\"x1_7\", output_filters=filters)\n\n    if upsampling_layers:\n        x1_8_pre = UpSampling2D(interpolation=\"bilinear\", name=\"x1_8_Upsample\")(x1_7)\n    else:\n        x1_8_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding=\"same\", activation=\"relu\", kernel_initializer=\"glorot_normal\", name=\"x1_8_ConvT\")(x1_7)\n    x1_8_add = Add(name=\"x1_8_Add\")([x1_2_pre, x1_8_pre])\n    x1_8 = residual_bottleneck_module(x1_8_add, prefix=\"x1_8\", output_filters=filters)\n\n    if upsampling_layers:\n        x1_9_pre = UpSampling2D(interpolation=\"bilinear\", name=\"x1_9_Upsample\")(x1_8)\n    else:\n        x1_9_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding=\"same\", activation=\"relu\", kernel_initializer=\"glorot_normal\", name=\"x1_9_ConvT\")(x1_8)\n    x1_9_add = Add(name=\"x1_9_Add\")([x1_1_pre, x1_9_pre])\n    x1_9 = residual_bottleneck_module(x1_9_add, prefix=\"x1_9\", output_filters=filters)\n\n    #############\n\n    x2_1_pre = residual_bottleneck_module(x1_9, prefix=\"x2_1\", output_filters=filters)\n    x2_1 = MaxPooling2D(pool_size=2, strides=2, padding=\"same\", name=\"x2_1_pool\")(x2_1_pre)\n\n    x2_2_pre = residual_bottleneck_module(x2_1, prefix=\"x2_2\", output_filters=filters)\n    x2_2 = MaxPooling2D(pool_size=2, strides=2, padding=\"same\", name=\"x2_2_pool\")(x2_2_pre)\n\n    x2_3_pre = residual_bottleneck_module(x2_2, prefix=\"x2_3\", output_filters=filters)\n    x2_3 = MaxPooling2D(pool_size=2, strides=2, padding=\"same\", name=\"x2_3_pool\")(x2_3_pre)\n\n    x2_4_pre = residual_bottleneck_module(x2_3, prefix=\"x2_4\", output_filters=filters)\n    x2_4 = MaxPooling2D(pool_size=2, strides=2, padding=\"same\", name=\"x2_4_pool\")(x2_4_pre)\n\n\n    x2_5 = residual_bottleneck_module(x2_4, prefix=\"x2_5\", output_filters=filters)\n\n\n    if upsampling_layers:\n        x2_6_pre = UpSampling2D(interpolation=\"bilinear\", name=\"x2_6_Upsample\")(x2_5)\n    else:\n        x2_6_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding=\"same\", activation=\"relu\", kernel_initializer=\"glorot_normal\", name=\"x2_6_ConvT\")(x2_5)\n    x2_6_add = Add(name=\"x2_6_Add\")([x2_4_pre, x2_6_pre])\n    x2_6 = residual_bottleneck_module(x2_6_add, prefix=\"x2_6\", output_filters=filters)\n\n    if upsampling_layers:\n        x2_7_pre = UpSampling2D(interpolation=\"bilinear\", name=\"x2_7_Upsample\")(x2_6)\n    else:\n        x2_7_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding=\"same\", activation=\"relu\", kernel_initializer=\"glorot_normal\", name=\"x2_7_ConvT\")(x2_6)\n    x2_7_add = Add(name=\"x2_7_Add\")([x2_3_pre, x2_7_pre])\n    x2_7 = residual_bottleneck_module(x2_7_add, prefix=\"x2_7\", output_filters=filters)\n\n    if upsampling_layers:\n        x2_8_pre = UpSampling2D(interpolation=\"bilinear\", name=\"x2_8_Upsample\")(x2_7)\n    else:\n        x2_8_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding=\"same\", activation=\"relu\", kernel_initializer=\"glorot_normal\", name=\"x2_8_ConvT\")(x2_7)\n    x2_8_add = Add(name=\"x2_8_Add\")([x2_2_pre, x2_8_pre])\n    x2_8 = residual_bottleneck_module(x2_8_add, prefix=\"x2_8\", output_filters=filters)\n\n    if upsampling_layers:\n        x2_9_pre = UpSampling2D(interpolation=\"bilinear\", name=\"x2_9_Upsample\")(x2_8)\n    else:\n        x2_9_pre = Conv2DTranspose(filters=filters, kernel_size=3, strides=2, padding=\"same\", activation=\"relu\", kernel_initializer=\"glorot_normal\", name=\"x2_9_ConvT\")(x2_8)\n    x2_9_add = Add(name=\"x2_9_Add\")([x2_1_pre, x2_9_pre])\n    x2_9 = residual_bottleneck_module(x2_9_add, prefix=\"x2_9\", output_filters=filters)\n\n    #############\n\n    x_out1 = residual_bottleneck_module(x1_9, output_filters=output_channels, bottleneck_factor=1, prefix=\"x_out1\", activation=\"linear\")\n    x_out2 = residual_bottleneck_module(x2_9, output_filters=output_channels, bottleneck_factor=1, prefix=\"x_out2\", activation=\"linear\")\n\n    # Compile\n    model = Model(inputs=x_in, outputs=[x_out1, x_out2], name=\"StackedHourglass\")\n    model.compile(optimizer=Adam(amsgrad=amsgrad), loss=\"mean_squared_error\")\n\n    if summary:\n        model.summary()\n\n    return model\n"
  },
  {
    "path": "leap/plot_joints_single.m",
    "content": "function h = plot_joints_single(pts, segments, markerSize, lineWidth)\n%PLOT_JOINTS_SINGLE Plot joints on a single frame.\n% Usage:\n%   plot_joints_single(pts, segments)\n%   plot_joints_single(pts, segments, markerSize, lineWidth)\n% \n% Args:\n%   pts: \n%   segments (or skeleton): table with line segments or struct containing it\n%   markerSize: default: 20\n%   lineWidth: default: 2\n% \n% See also: \n\nif isfield(segments,'segments'); segments = segments.segments; end\nif nargin < 3 || isempty(markerSize); markerSize = 20; end\nif nargin < 4 || isempty(lineWidth); lineWidth = 2; end\n\nh = gobjects(numel(segments.joints_idx),1);\nfor i = 1:numel(segments.joints_idx)\n    h(i) = plotpts(pts(segments.joints_idx{i},:),'.-', ...\n        'Color',segments.color{i},'MarkerSize',markerSize,'LineWidth',lineWidth);\nend\n\nif nargout < 1; clear h; end\n\nend\n"
  },
  {
    "path": "leap/predict_box.m",
    "content": "function preds = predict_box(box, modelPath, saveConfmaps)\n%PREDICT_BOX Evaluates model predictions on a stack of frames. Wrapper for predict_box.py.\n% Usage:\n%   preds = predict_box(box, modelPath)\n%   preds = predict_box(box, modelPath, saveConfmaps)\n%\n% Args:\n%   box: 4-D array or path to HDF5 file with '/box' dataset\n%   modelPath: path to model weights\n%   saveConfmaps: if true, returns full confidence maps in addition to\n%                 peaks (default: false). Very slow and memory intensive!\n%   \n% Returns:\n%   preds: struct containing results from model prediction\n%        .positions_pred: 3-D array of (parts x [X Y] x frames) indicating\n%                         peak positions for each confidence map in image coordinates\n%        .conf_pred: 2-D array of (parts x frames) with the confidence map\n%                    value at the peak pixel for detecting bad predictions\n%        .confmaps: 4-D array of confidence maps returned if saveConfmaps\n%                   is set true\n\nif nargin < 3 || isempty(saveConfmaps); saveConfmaps = false; end\n\n% Process args\ndelete_box = false;\nis_singleton = false;\nif ischar(box)\n    boxPath = box;\nelse\n    boxPath = [tempname '.h5'];\n    \n    if numel(size(box)) < 4\n        box = repmat(box,[1 1 1 2]);\n        is_singleton = true;\n    end\n    \n    h5save(boxPath, box)\n    delete_box = true;\nend\n\n% Generate temporary output filename\noutPath = [tempname '.h5'];\n\n% Build command line args\ncmd = {\n    'python'\n    ['\"' ff(funpath(true), 'predict_box.py') '\"']\n    ['\"' boxPath '\"']\n    ['\"' modelPath '\"']\n    ['\"' outPath '\"']\n    };\nif saveConfmaps\n    cmd{end+1} = '--save-confmaps';\nend\ndisp(strjoin(cmd))\n\n% Predict\ntry\n    exit_code = system(strjoin(cmd));\ncatch ME\n    if delete_box && exists(boxPath); delete(boxPath); end\n    rethrow(ME)\nend\nif delete_box && exists(boxPath); delete(boxPath); end\n\n\n% Read data back in\ntry\n    preds = h5readgroup(outPath);\ncatch ME\n    if exists(outPath); delete(outPath); end\n    rethrow(ME)\nend\nif exists(outPath); delete(outPath); end\n\n% Adjust for 0-based indexing\npreds.positions_pred = single(preds.positions_pred) + 1;\n\n% Rescale confidence maps to correct range prior to quantization\nif saveConfmaps && isfield(preds.Attributes, 'confmaps')\n    c_min = preds.Attributes.confmaps.range_min;\n    c_max = preds.Attributes.confmaps.range_max;\n    preds.confmaps = rescale(single(preds.confmaps) / 255, c_min, c_max);\nend\n\n% Adjust for singleton input\nif is_singleton\n    preds.positions_pred = preds.positions_pred(:,:,1);\n    preds.conf_pred = preds.conf_pred(:,1);\n    if isfield(preds,'confmaps')\n        preds.confmaps = preds.confmaps(:,:,:,1);\n    end\nend\n\nend\n"
  },
  {
    "path": "leap/predict_box.py",
    "content": "import h5py\nimport numpy as np\nimport os\nfrom time import time\nimport keras\nimport keras.models\nfrom keras.layers import Lambda\nimport tensorflow as tf\nimport re\nfrom clize import run\n\nfrom leap.utils import find_weights, find_best_weights, preprocess\nfrom leap.layers import Maxima2D\n\ndef tf_find_peaks(x):\n    \"\"\" Finds the maximum value in each channel and returns the location and value.\n    Args:\n        x: rank-4 tensor (samples, height, width, channels)\n\n    Returns:\n        peaks: rank-3 tensor (samples, [x, y, val], channels)\n    \"\"\"\n\n    # Store input shape\n    in_shape = tf.shape(x)\n\n    # Flatten height/width dims\n    flattened = tf.reshape(x, [in_shape[0], -1, in_shape[-1]])\n\n    # Find peaks in linear indices\n    idx = tf.argmax(flattened, axis=1)\n\n    # Convert linear indices to subscripts\n    rows = tf.floor_div(tf.cast(idx,tf.int32), in_shape[1])\n    cols = tf.floormod(tf.cast(idx,tf.int32), in_shape[1])\n\n    # Dumb way to get actual values without indexing\n    vals = tf.reduce_max(flattened, axis=1)\n\n    # Return N x 3 x C tensor\n    return tf.stack([\n        tf.cast(cols, tf.float32),\n        tf.cast(rows, tf.float32),\n        vals\n    ], axis=1)\n\n\ndef convert_to_peak_outputs(model, include_confmaps=False):\n    \"\"\" Creates a new Keras model with a wrapper to yield channel peaks from rank-4 tensors. \"\"\"\n    if type(model.output) == list:\n        confmaps = model.output[-1]\n    else:\n        confmaps = model.output\n\n    if include_confmaps:\n        return keras.Model(model.input, [Lambda(tf_find_peaks)(confmaps), confmaps])\n    else:\n        # return keras.Model(model.input, Lambda(tf_find_peaks)(confmaps))\n        return keras.Model(model.input, Maxima2D()(confmaps))\n\n\ndef predict_box(box_path, model_path, out_path, *, box_dset=\"/box\", epoch=None, verbose=True, overwrite=False, save_confmaps=False, batch_size=32):\n    \"\"\"\n    Predict and save peak coordinates for a box.\n\n    :param box_path: path to HDF5 file with box dataset\n    :param model_path: path to Keras weights file or run folder with weights subfolder\n    :param out_path: path to HDF5 file to save results to\n    :param box_dset: name of HDF5 dataset containing box images\n    :param epoch: epoch to use if run folder provided instead of Keras weights file\n    :param verbose: if True, prints some info and statistics during procesing\n    :param overwrite: if True and out_path exists, file will be overwritten\n    :param save_confmaps: if True, saves the full confidence maps as additional datasets in the output file (very slow)\n    :param batch_size: number of samples to evaluate at once per batch (see keras.Model API)\n    \"\"\"\n\n    if verbose:\n        print(\"model_path:\", model_path)\n\n    # Find model weights\n    model_name = None\n    weights_path = model_path\n    if os.path.isdir(model_path):\n        model_name = os.path.basename(model_path)\n\n        weights_paths, epochs, val_losses = find_weights(model_path)\n\n        if epoch == None and len(val_losses) > 0:\n            weights_path = weights_paths[np.argmin(val_losses)]\n        elif epoch == \"final\" or (epoch == None and len(val_losses) == 0):\n            weights_path = os.path.join(model_path, \"final_model.h5\")\n        else:\n            weights_path = weights_paths[epoch]\n\n    # Input data\n    box = h5py.File(box_path,\"r\")[box_dset]\n    num_samples = box.shape[0]\n    if verbose:\n        print(\"Input:\", box_path)\n        print(\"box.shape:\", box.shape)\n\n    # Create output path\n    if out_path[-3:] != \".h5\":\n        if model_name == None:\n            out_path = os.path.join(out_path, os.path.basename(box_path))\n        else:\n            out_path = os.path.join(out_path, model_name, os.path.basename(box_path))\n        os.makedirs(os.path.dirname(out_path), exist_ok=True)\n\n    model_name = os.path.basename(model_path)\n\n    if verbose:\n        print(\"Output:\", out_path)\n\n    t0_all = time()\n    if os.path.exists(out_path):\n        if overwrite:\n            os.remove(out_path)\n            print(\"Deleted existing output.\")\n        else:\n            print(\"Error: Output path already exists.\")\n            return\n\n    # Load and prepare model\n    model = keras.models.load_model(weights_path)\n    model_peaks = convert_to_peak_outputs(model, include_confmaps=save_confmaps)\n    if verbose:\n        print(\"weights_path:\", weights_path)\n        print(\"Loaded model: %d layers, %d params\" % (len(model.layers), model.count_params()))\n\n    # Load data and preprocess (normalize)\n    t0 = time()\n    X = preprocess(box[:])\n    if verbose:\n        print(\"Loaded [%.1fs]\" % (time() - t0))\n\n    # Evaluate\n    t0 = time()\n    if save_confmaps:\n        Ypk, confmaps = model_peaks.predict(X, batch_size=batch_size)\n\n        # Quantize\n        confmaps_min = confmaps.min()\n        confmaps_max = confmaps.max()\n        confmaps = (confmaps - confmaps_min) / (confmaps_max - confmaps_min)\n        confmaps = (confmaps * 255).astype('uint8')\n\n        # Reshape\n        confmaps = np.transpose(confmaps, (0, 3, 2, 1))\n    else:\n        Ypk = model_peaks.predict(X, batch_size=batch_size)\n    prediction_runtime = time() - t0\n    if verbose:\n        print(\"Predicted [%.1fs]\" % prediction_runtime)\n        print(\"Prediction performance: %.3f FPS\" % (num_samples / prediction_runtime))\n\n    # Save\n    t0 = time()\n    with h5py.File(out_path, \"w\") as f:\n        f.attrs[\"num_samples\"] = num_samples\n        f.attrs[\"img_size\"] = X.shape[1:]\n        f.attrs[\"box_path\"] = box_path\n        f.attrs[\"box_dset\"] = box_dset\n        f.attrs[\"model_path\"] = model_path\n        f.attrs[\"weights_path\"] = weights_path\n        f.attrs[\"model_name\"] = model_name\n\n        ds_pos = f.create_dataset(\"positions_pred\", data=Ypk[:,:2,:].astype(\"int32\"), compression=\"gzip\", compression_opts=1)\n        ds_pos.attrs[\"description\"] = \"coordinate of peak at each sample\"\n        ds_pos.attrs[\"dims\"] = \"(sample, [x, y], joint) === (sample, [column, row], joint)\"\n\n        ds_conf = f.create_dataset(\"conf_pred\", data=Ypk[:,2,:].squeeze(), compression=\"gzip\", compression_opts=1)\n        ds_conf.attrs[\"description\"] = \"confidence map value in [0, 1.0] at peak\"\n        ds_conf.attrs[\"dims\"] = \"(sample, joint)\"\n\n        if save_confmaps:\n            ds_confmaps = f.create_dataset(\"confmaps\", data=confmaps, compression=\"gzip\", compression_opts=1)\n            ds_confmaps.attrs[\"description\"] = \"confidence maps\"\n            ds_confmaps.attrs[\"dims\"] = \"(sample, channel, width, height)\"\n            ds_confmaps.attrs[\"range_min\"] = confmaps_min\n            ds_confmaps.attrs[\"range_max\"] = confmaps_max\n\n        total_runtime = time() - t0_all\n        f.attrs[\"total_runtime_secs\"] = total_runtime\n        f.attrs[\"prediction_runtime_secs\"] = prediction_runtime\n\n    if verbose:\n        print(\"Saved [%.1fs]\" % (time() - t0))\n\n        print(\"Total runtime: %.1f mins\" % (total_runtime / 60))\n        print(\"Total performance: %.3f FPS\" % (num_samples / total_runtime))\n\n\nif __name__ == \"__main__\":\n    run(predict_box)\n"
  },
  {
    "path": "leap/pts2confmaps.m",
    "content": "function confmaps = pts2confmaps(pts, sz, sigma, normalize)\n%PTS2CONFMAPS Generate confidence maps centered at specified points.\n% Usage:\n%   confmaps = pts2confmaps(pts, sz, sigma)\n%\n% Args:\n%   pts: N x 2 or cell array of {N1 x 2, N2 x 2, ...}, where each cell will\n%       correspond to a single channel to create multipoint confidence maps\n%   sz: [rows cols]\n%   sigma: filter size (default: 5)\n%   normalize: outputs maps in [0, 1] rather than PDF (default: true)\n%\n% See also: label_joints\n\nif ~iscell(pts); pts = arr2cell(pts,1); end\nif nargin < 3 || isempty(sigma); sigma = 5; end\nif nargin < 4 || isempty(normalize); normalize = true; end\n\nconfmaps = NaN(sz(1), sz(2), numel(pts));\nxv = 1:sz(2); yv = 1:sz(1);\n[XX,YY] = meshgrid(xv,yv);\n\nfor i = 1:numel(pts)\n    x = permute(pts{i}(:,1),[2 3 1]);\n    y = permute(pts{i}(:,2),[2 3 1]);\n%     confmaps(:,:,i) = sum(exp(-((YY-y).^2 + (XX-x).^2)./(2*sigma^2)),3);\n    confmaps(:,:,i) = max(exp(-((YY-y).^2 + (XX-x).^2)./(2*sigma^2)),[],3);\nend\n\nif ~normalize\n    confmaps = confmaps ./ (sigma * sqrt(2*pi));\nend\n\nend\n"
  },
  {
    "path": "leap/test_leap.m",
    "content": "function works = test_leap()\n%TEST_LEAP Checks whether LEAP is properly installed.\n% Usage:\n%   test_leap\n%   works = test_leap % returns true/false\n% \n% See also: install_leap\n\nworks = true;\n\n% Check if we can import the LEAP package from anywhere\ncdCmd = ['cd \"' matlabroot '\"'];\nif ispc(); cdCmd = ['cd /D \"' matlabroot '\"']; end\n[status,msg] = system([cdCmd ' && python -c \"import leap\"']);\n\nif status ~= 0\n    works = false;\nend\n\nif nargout == 0\n    if works\n        printf('Test LEAP successful!')\n    else\n        disp('Unable to import LEAP python package. Make sure LEAP and its dependencies are installed.')\n        disp('Go to the base LEAP directory containing setup.py and run from MATLAB:')\n        disp('    !pip install -e .')\n        disp('Or try the MATLAB installer:')\n        disp('    install_leap')\n    end\n    clear works\nend\n\nend\n"
  },
  {
    "path": "leap/toolbox/aliases/alims.m",
    "content": "function varargout = alims(X)\n%ALIMS Alias for arange.\n% Usage:\n%   R = alims(X)\n%   [min_val, max_val] = alims(X)\n%\n% See also: arange\n\nvarargout = wrap(@() arange(X), 1:max(1, nargout));\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/aliases/ff.m",
    "content": "function varargout = ff(varargin)\n    N = max(nargout,1);\n\tvarargout{1:N} = fullfile(varargin{:});\nend\n"
  },
  {
    "path": "leap/toolbox/aliases/h5file.m",
    "content": "function varargout = h5file(varargin)\n\tvarargout{1:nargout} = hdf5prop(varargin{:});\nend\n"
  },
  {
    "path": "leap/toolbox/aliases/imgsc.m",
    "content": "function h = imgsc(I, varargin)\n%IMGSC Alias for imagesc for images.\n% Usage:\n%   imgsc(I)\n%   imgsc(I, ...)\n%\n% See also: imagesc, sc\n\n% figure('KeyPressFcn',@KeyPressFcn_cb)\nfigclosekey\nh = imagesc(I, varargin{:});\nH = size(I,1); W = size(I,2);\nif  max([H W]) / min([H W]) < 2 || all([H W] < 25)\n    axis image\nend\ncolorbar\n\nif nargout < 1; clear h; end\n\nend"
  },
  {
    "path": "leap/toolbox/aliases/repext.m",
    "content": "function varargout = repext(varargin)\n\tvarargout{1:max(nargout,1)} = extrep(varargin{:});\nend\n"
  },
  {
    "path": "leap/toolbox/graphics/FEX-settingsdlg/settingsdlg.m",
    "content": "function [settings, button] = settingsdlg(varargin)\n% SETTINGSDLG             Default dialog to produce a settings-structure\n%\n% settings = SETTINGSDLG('fieldname', default_value, ...) creates a modal\n% dialog box that returns a structure formed according to user input. The\n% input should be given in the form of 'fieldname', default_value - pairs,\n% where 'fieldname' is the fieldname in the structure [settings], and\n% default_value the initial value displayed in the dialog box.\n%\n% SETTINGSDLG uses UIWAIT to suspend execution until the user responds.\n%\n% settings = SETTINGSDLG(settings) uses the structure [settings] to form\n% the various input fields. This is the most basic (and limited) usage of\n% SETTINGSDLG.\n%\n% [settings, button] = SETTINGSDLG(settings) returns which button was\n% pressed, in addition to the (modified) structure [settings]. Either 'ok',\n% 'cancel' or [] are possible values. The empty output means that the\n% dialog was closed before either Cancel or OK were pressed.\n%\n% SETTINGSDLG('title', 'window_title') uses 'window_title' as the dialog's\n% title. The default is 'Adjust settings'.\n%\n% SETTINGSDLG('description', 'brief_description',...) starts the dialog box\n% with 'brief_description', followed by the input fields.\n%\n% SETTINGSDLG('windowposition', P, ...) positions the dialog box according to\n% the string or vector [P]; see movegui() for valid values.\n%\n% SETTINGSDLG( {'display_string', 'fieldname'}, default_value,...) uses the\n% 'display_string' in the dialog box, while assigning the corresponding\n% user-input to fieldname 'fieldname'.\n%\n% SETTINGSDLG(..., 'checkbox_string', true, ...) displays a checkbox in\n% stead of the default edit box, and SETTINGSDLG('fieldname', {'string1',\n% 'string2'},... ) displays a popup box with the strings given in\n% the second cell-array.\n%\n% Additionally, you can put [..., 'separator', 'seperator_string',...]\n% anywhere in the argument list, which will divide all the arguments into\n% sections, with section headings 'seperator_string'.\n%\n% You can also modify the display behavior in the case of checkboxes. When\n% defining checkboxes with a 2-element logical array, the second boolean\n% determines whether all fields below that checkbox are initially disabled\n% (true) or not (false).\n%\n% Example:\n%\n% [settings, button] = settingsdlg(...\n%     'Description', 'This dialog will set the parameters used by FMINCON()',...\n%     'title'      , 'FMINCON() options',...\n%     'separator'  , 'Unconstrained/General',...\n%     {'This is a checkbox'; 'Check'}, [true true],...\n%     {'Tolerance X';'TolX'}, 1e-6,...\n%     {'Tolerance on Function';'TolFun'}, 1e-6,...\n%     'Algorithm'  , {'active-set','interior-point'},...\n%     'separator'  , 'Constrained',...\n%     {'Tolerance on Constraints';'TolCon'}, 1e-6)\n%\n% See also inputdlg, dialog, errordlg, helpdlg, listdlg, msgbox, questdlg, textwrap,\n% uiwait, warndlg.\n\n\n% Please report bugs and inquiries to:\n%\n% Name   : Rody P.S. Oldenhuis\n% E-mail : oldenhuis@gmail.com\n% Licence: 2-clause BSD (See Licence.txt)\n\n\n% If you find this work useful, please consider a donation:\n% https://www.paypal.me/RodyO/3.5\n\n    %% Initialize\n\n    % errortraps\n    narg = nargin;\n    if verLessThan('MATLAB', '8.6')\n        error(nargchk(1, inf, narg, 'struct')); %#ok<NCHKN>\n    else\n        narginchk(1, inf);\n    end\n\n    % parse input (+errortrap)\n    have_settings = 0;\n    if isstruct(varargin{1})\n        settings = varargin{1}; have_settings = 1; end\n    if (narg == 1)\n        if isstruct(varargin{1})\n            parameters = fieldnames(settings);\n            values = cellfun(@(x)settings.(x), parameters, 'UniformOutput', false);\n        else\n            error('settingsdlg:incorrect_input',...\n                'When passing a single argument, that argument must be a structure.')\n        end\n    else\n        parameters = varargin(1+have_settings : 2 : end);\n        values     = varargin(2+have_settings : 2 : end);\n    end\n\n    % Initialize data\n    button = [];\n    fields = cell(numel(parameters),1);\n    tags   = fields;\n\n    % Fill [settings] with default values & collect data\n    for ii = 1:numel(parameters)\n\n        % Extract fields & tags\n        if iscell(parameters{ii})\n            tags{ii}   = parameters{ii}{1};\n            fields{ii} = parameters{ii}{2};\n        else\n            % More errortraps\n            if ~ischar(parameters{ii})\n                error('settingsdlg:nonstring_parameter',...\n                'Arguments should be given as [''parameter'', value,...] pairs.')\n            end\n            tags{ii}   = parameters{ii};\n            fields{ii} = parameters{ii};\n        end\n\n        % More errortraps\n        if ~ischar(fields{ii})\n            error('settingsdlg:fieldname_not_char',...\n                'Fieldname should be a string.')\n        end\n        if ~ischar(tags{ii})\n            error('settingsdlg:tag_not_char',...\n                'Display name should be a string.')\n        end\n\n        % NOTE: 'Separator' is now in 'fields' even though\n        % it will not be used as a fieldname\n\n        % Make sure all fieldnames are properly formatted\n        % (alternating capitals, no whitespace)\n        if ~strcmpi(fields{ii}, {'Separator';'Title';'Description'})\n            whitespace = isspace(fields{ii});\n            capitalize = circshift(whitespace,[0,1]);\n            fields{ii}(capitalize) = upper(fields{ii}(capitalize));\n            fields{ii} = fields{ii}(~whitespace);\n            % insert associated value in output\n            if iscell(values{ii})\n                settings.(fields{ii}) = values{ii}{1};\n            elseif (length(values{ii}) > 1)\n                settings.(fields{ii}) = values{ii}(1);\n            else\n                settings.(fields{ii}) = values{ii};\n            end\n        end\n    end\n\n    % Avoid (some) confusion\n    clear parameters\n\n    % Use default colorscheme from the OS\n    bgcolor = get(0, 'defaultUicontrolBackgroundColor');\n    % Default fontsize\n    fontsize = get(0, 'defaultuicontrolfontsize');\n    % Edit-bgcolor is platform-dependent.\n    % MS/Windows: white.\n    % UNIX: same as figure bgcolor\n%     if ispc, edit_bgcolor = 'White';\n%     else     edit_bgcolor = bgcolor;\n%     end\n\n% TODO: not really applicable since defaultUicontrolBackgroundColor\n% doesn't really seem to work on Unix...\nedit_bgcolor = 'White';\n\n    % Get basic window properties\n    title         = getValue('Adjust settings', 'Title');\n    description   = getValue( [], 'Description');\n    total_width   = getValue(325, 'WindowWidth');\n    control_width = getValue(100, 'ControlWidth');\n\n    % Window positioning:\n    % Put the window in the center of the screen by default.\n    % This will usually work fine, except on some  multi-monitor setups.\n    scz  = get(0, 'ScreenSize');\n    scxy = round(scz(3:4)/2-control_width/2);\n    scx  = min(scz(3),max(1,scxy(1)));\n    scy  = min(scz(4),max(1,scxy(2)));\n\n    % String to pass on to movegui\n    window_position = getValue('center', 'WindowPosition');\n\n\n    % Calculate best height for all uicontrol()\n    control_height = max(18, (fontsize+6));\n\n    % Calculate figure height (will be adjusted later according to description)\n    total_height = numel(fields)*1.25*control_height + ... % to fit all controls\n                     1.5*control_height + 20; % to fit \"OK\" and \"Cancel\" buttons\n\n    % Total number of separators\n    num_separators = nnz(strcmpi(fields,'Separator'));\n\n    % Draw figure in background\n    fighandle = figure(...\n         'integerhandle'   , 'off',...         % use non-integers for the handle (prevents accidental plots from going to the dialog)\n         'Handlevisibility', 'off',...         % only visible from within this function\n         'position'        , [scx, scy, total_width, total_height],...% figure position\n         'visible'         , 'off',...         % hide the dialog while it is being constructed\n         'backingstore'    , 'off',...         % DON'T save a copy in the background\n         'resize'          , 'off', ...        % but just keep it resizable\n         'renderer'        , 'zbuffer', ...    % best choice for speed vs. compatibility\n         'WindowStyle'     ,'modal',...        % window is modal\n         'units'           , 'pixels',...      % better for drawing\n         'DockControls'    , 'off',...         % force it to be non-dockable\n         'name'            , title,...         % dialog title\n         'menubar'         ,'none', ...        % no menubar of course\n         'toolbar'         ,'none', ...        % no toolbar\n         'NumberTitle'     , 'off',...         % \"Figure 1.4728...:\" just looks corny\n         'color'           , bgcolor);         % use default colorscheme\n\n    %% Draw all required uicontrols(), and unhide window\n\n    % Define X-offsets (different when separators are used)\n    separator_offset_X = 2;\n    if num_separators > 0\n        text_offset_X = 20;\n        text_width = (total_width-control_width-text_offset_X);\n    else\n        text_offset_X = separator_offset_X;\n        text_width = (total_width-control_width);\n    end\n\n    % Handle description\n    description_offset = 0;\n    if ~isempty(description)\n\n        % create textfield (negligible height initially)\n        description_panel = uicontrol(...\n            'parent'  , fighandle,...\n            'style'   , 'text',...\n            'Horizontalalignment', 'left',...\n            'position', [separator_offset_X,...\n                         total_height,total_width,1]);\n\n        % wrap the description\n        description = textwrap(description_panel, {description});\n\n        % adjust the height of the figure\n        textheight = size(description,1)*(fontsize+6);\n        description_offset = textheight + 20;\n        total_height = total_height + description_offset;\n        set(fighandle,...\n            'position', [scx, scy, total_width, total_height])\n\n        % adjust the position of the textfield and insert the description\n        set(description_panel, ...\n            'string'  , description,...\n            'position', [separator_offset_X, total_height-textheight, ...\n                         total_width, textheight]);\n    end\n\n    % Define Y-offsets (different when descriptions are used)\n    control_offset_Y = total_height-control_height-description_offset;\n\n    % initialize loop\n    controls = zeros(numel(tags)-num_separators,1);\n    ii = 1;             sep_ind = 1;\n    enable = 'on';      separators = zeros(num_separators,1);\n\n    % loop through the controls\n    if numel(tags) > 0\n        while true\n\n            % Should we draw a separator?\n            if strcmpi(tags{ii}, 'Separator')\n\n                % Print separator\n                uicontrol(...\n                    'style'   , 'text',...\n                    'parent'  , fighandle,...\n                    'string'  , values{ii},...\n                    'horizontalalignment', 'left',...\n                    'fontweight', 'bold',...\n                    'position', [separator_offset_X,control_offset_Y-4, ...\n                    total_width, control_height]);\n\n                % remove separator, but save its position\n                fields(ii) = [];\n                tags(ii)   = [];  separators(sep_ind) = ii;\n                values(ii) = [];  sep_ind = sep_ind + 1;\n\n                % reset enable (when neccessary)\n                if strcmpi(enable, 'off')\n                    enable = 'on'; end\n\n                % NOTE: DON'T increase loop index\n\n            % ... or a setting?\n            else\n\n                % logicals: use checkbox\n                if islogical(values{ii})\n\n                    % First draw control\n                    controls(ii) = uicontrol(...\n                        'style'   , 'checkbox',...\n                        'parent'  , fighandle,...\n                        'enable'  , enable,...\n                        'string'  , tags{ii},...\n                        'value'   , values{ii}(1),...\n                        'position', [text_offset_X,control_offset_Y-4, ...\n                        total_width, control_height]);\n\n                    % Should everything below here be OFF?\n                    if (length(values{ii})>1)\n                        % turn next controls off when asked for\n                        if values{ii}(2)\n                            enable = 'off'; end\n                        % Turn on callback function\n                        set(controls(ii),...\n                            'Callback', @(varargin) EnableDisable(ii,varargin{:}));\n                    end\n\n                % doubles      : use edit box\n                % cells        : use popup\n                % cell-of-cells: use table\n                else\n                    % First print parameter\n                    uicontrol(...\n                        'style'   , 'text',...\n                        'parent'  , fighandle,...\n                        'string'  , [tags{ii}, ':'],...\n                        'horizontalalignment', 'left',...\n                        'position', [text_offset_X,control_offset_Y-4, ...\n                        text_width, control_height]);\n\n                    % Popup, edit box or table?\n                    style = 'edit';\n                    draw_table = false;\n                    if iscell(values{ii})\n                        style = 'popup';\n                        if all(cellfun('isclass', values{ii}, 'cell'))\n                            draw_table = true; end\n                    end\n\n                    % Draw appropriate control\n                    if ~draw_table\n                        controls(ii) = uicontrol(...\n                            'enable'  , enable,...\n                            'style'   , style,...\n                            'Background', edit_bgcolor,...\n                            'parent'  , fighandle,...\n                            'string'  , values{ii},...\n                            'position', [text_width,control_offset_Y,...\n                            control_width, control_height]);\n                    else\n                        % TODO\n                        % ...table? ...radio buttons? How to do this?\n                        warning(...\n                            'settingsdlg:not_yet_implemented',...\n                            'Treatment of cells is not yet implemented.');\n\n                    end\n                end\n\n                % increase loop index\n                ii = ii + 1;\n            end\n\n            % end loop?\n            if ii > numel(tags)\n                break, end\n\n            % Decrease offset\n            control_offset_Y = control_offset_Y - 1.25*control_height;\n        end\n    end\n\n    % Draw cancel button\n    uicontrol(...\n        'style'   , 'pushbutton',...\n        'parent'  , fighandle,...\n        'string'  , 'Cancel',...\n        'position', [separator_offset_X,2, total_width/2.5,control_height*1.5],...\n        'Callback', @Cancel)\n\n    % Draw OK button\n    uicontrol(...\n        'style'   , 'pushbutton',...\n        'parent'  , fighandle,...\n        'string'  , 'OK',...\n        'position', [total_width*(1-1/2.5)-separator_offset_X,2, ...\n                     total_width/2.5,control_height*1.5],...\n        'Callback', @OK)\n\n    % move to center of screen and make visible\n    movegui(fighandle, window_position);\n    set(fighandle, 'Visible', 'on');\n\n    % WAIT until OK/Cancel is pressed\n    uiwait(fighandle);\n\n\n\n    %% Helper funcitons\n\n    % Get a value from the values array:\n    % - if it does not exist, return the default value\n    % - if it exists, assign it and delete the appropriate entries from the\n    %   data arrays\n    function val = getValue(default, tag)\n        index = strcmpi(fields, tag);\n        if any(index)\n            val = values{index};\n            values(index) = [];\n            fields(index) = [];\n            tags(index)   = [];\n        else\n            val = default;\n        end\n    end\n\n    %% callback functions\n\n    % Enable/disable controls associated with (some) checkboxes\n    function EnableDisable(which, varargin) %#ok<VANUS>\n\n        % find proper range of controls to switch\n        if (num_separators > 1)\n             last_control = separators(find(separators > which,1)) - 1;\n             if isempty(last_control); last_control = numel(controls); end\n             \n             range = (which+1):(last_control);\n        else\n            range = (which+1):numel(controls);\n        end\n\n        % enable/disable these controls\n        if strcmpi(get(controls(range(1)), 'enable'), 'off')\n            set(controls(range), 'enable', 'on')\n        else\n            set(controls(range), 'enable', 'off')\n        end\n    end\n\n    % OK button:\n    % - update fields in [settings]\n    % - assign [button] output argument ('ok')\n    % - kill window\n    function OK(varargin) %#ok<VANUS>\n\n        % button pressed\n        button = 'OK';\n\n        % fill settings\n        for i = 1:numel(controls)\n\n            % extract current control's string, value & type\n            str   = get(controls(i), 'string');\n            val   = get(controls(i), 'value');\n            style = get(controls(i), 'style');\n\n            % popups/edits\n            if ~strcmpi(style, 'checkbox')\n                % extract correct string (popups only)\n                if strcmpi(style, 'popupmenu'), str = str{val}; end\n                % try to convert string to double\n                val = str2double(str);\n                % insert this double in [settings]. If it was not a\n                % double, insert string instead\n                if ~isnan(val), settings.(fields{i}) = val;\n                else            settings.(fields{i}) = str;\n                end\n\n            % checkboxes\n            else\n                % we can insert value immediately\n                settings.(fields{i}) = val;\n            end\n        end\n\n        %  kill window\n        delete(fighandle);\n    end\n\n    % Cancel button:\n    % - assign [button] output argument ('cancel')\n    % - delete figure (so: return default settings)\n    function Cancel(varargin) %#ok<VANUS>\n        button = 'cancel';\n        delete(fighandle);\n    end\n\nend\n"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/+mixin/Container.m",
    "content": "classdef Container < handle\n    %uix.mixin.Container  Container mixin\n    %\n    %  uix.mixin.Container is a mixin class used by containers to provide\n    %  various properties and template methods.\n    \n    %  Copyright 2009-2016 The MathWorks, Inc.\n    %  $Revision: 1358 $ $Date: 2016-09-14 11:34:17 +0100 (Wed, 14 Sep 2016) $\n    \n    properties( Dependent, Access = public )\n        Contents % contents in layout order\n    end\n    \n    properties( Access = public, Dependent, AbortSet )\n        Padding % space around contents, in pixels\n    end\n    \n    properties( Access = protected )\n        Contents_ = gobjects( [0 1] ) % backing for Contents\n        Padding_ = 0 % backing for Padding\n    end\n    \n    properties( Dependent, Access = protected )\n        Dirty % needs redraw\n    end\n    \n    properties( Access = private )\n        Dirty_ = false % backing for Dirty\n        FigureObserver % observer\n        FigureListener % listener\n        ChildObserver % observer\n        ChildAddedListener % listener\n        ChildRemovedListener % listener\n        SizeChangedListener % listener\n        ActivePositionPropertyListeners = cell( [0 1] ) % listeners\n    end\n    \n    methods\n        \n        function obj = Container()\n            %uix.mixin.Container  Initialize\n            %\n            %  c@uix.mixin.Container() initializes the container c.\n            \n            % Create observers and listeners\n            figureObserver = uix.FigureObserver( obj );\n            figureListener = event.listener( figureObserver, ...\n                'FigureChanged', @obj.onFigureChanged );\n            childObserver = uix.ChildObserver( obj );\n            childAddedListener = event.listener( ...\n                childObserver, 'ChildAdded', @obj.onChildAdded );\n            childRemovedListener = event.listener( ...\n                childObserver, 'ChildRemoved', @obj.onChildRemoved );\n            sizeChangedListener = event.listener( ...\n                obj, 'SizeChanged', @obj.onSizeChanged );\n            \n            % Store observers and listeners\n            obj.FigureObserver = figureObserver;\n            obj.FigureListener = figureListener;\n            obj.ChildObserver = childObserver;\n            obj.ChildAddedListener = childAddedListener;\n            obj.ChildRemovedListener = childRemovedListener;\n            obj.SizeChangedListener = sizeChangedListener;\n            \n            % Track usage\n            obj.track()\n            \n        end % constructor\n        \n    end % structors\n    \n    methods\n        \n        function value = get.Contents( obj )\n            \n            value = obj.Contents_;\n            \n        end % get.Contents\n        \n        function set.Contents( obj, value )\n            \n            % For those who can't tell a column from a row...\n            if isrow( value )\n                value = transpose( value );\n            end\n            \n            % Check\n            [tf, indices] = ismember( value, obj.Contents_ );\n            assert( isequal( size( obj.Contents_ ), size( value ) ) && ...\n                numel( value ) == numel( unique( value ) ) && all( tf ), ...\n                'uix:InvalidOperation', ...\n                'Property ''Contents'' may only be set to a permutation of itself.' )\n            \n            % Call reorder\n            obj.reorder( indices )\n            \n        end % set.Contents\n        \n        function value = get.Padding( obj )\n            \n            value = obj.Padding_;\n            \n        end % get.Padding\n        \n        function set.Padding( obj, value )\n            \n            % Check\n            assert( isa( value, 'double' ) && isscalar( value ) && ...\n                isreal( value ) && ~isinf( value ) && ...\n                ~isnan( value ) && value >= 0, ...\n                'uix:InvalidPropertyValue', ...\n                'Property ''Padding'' must be a non-negative scalar.' )\n            \n            % Set\n            obj.Padding_ = value;\n            \n            % Mark as dirty\n            obj.Dirty = true;\n            \n        end % set.Padding\n        \n        function value = get.Dirty( obj )\n            \n            value = obj.Dirty_;\n            \n        end % get.Dirty\n        \n        function set.Dirty( obj, value )\n            \n            if value\n                if obj.isDrawable() % drawable\n                    obj.redraw() % redraw now\n                else % not drawable\n                    obj.Dirty_ = true; % flag for future redraw\n                end\n            end\n            \n        end % set.Dirty\n        \n    end % accessors\n    \n    methods( Access = private, Sealed )\n        \n        function onFigureChanged( obj, ~, eventData )\n            %onFigureChanged  Event handler\n            \n            % Call template method\n            obj.reparent( eventData.OldFigure, eventData.NewFigure )\n            \n            % Redraw if possible and if dirty\n            if obj.Dirty_ && obj.isDrawable()\n                obj.redraw()\n                obj.Dirty_ = false;\n            end\n            \n        end % onFigureChanged\n        \n        function onChildAdded( obj, ~, eventData )\n            %onChildAdded  Event handler\n            \n            % Call template method\n            obj.addChild( eventData.Child )\n            \n        end % onChildAdded\n        \n        function onChildRemoved( obj, ~, eventData )\n            %onChildRemoved  Event handler\n            \n            % Do nothing if container is being deleted\n            if strcmp( obj.BeingDeleted, 'on' ), return, end\n            \n            % Call template method\n            obj.removeChild( eventData.Child )\n            \n        end % onChildRemoved\n        \n        function onSizeChanged( obj, ~, ~ )\n            %onSizeChanged  Event handler\n            \n            % Mark as dirty\n            obj.Dirty = true;\n            \n        end % onSizeChanged\n        \n        function onActivePositionPropertyChanged( obj, ~, ~ )\n            %onActivePositionPropertyChanged  Event handler\n            \n            % Mark as dirty\n            obj.Dirty = true;\n            \n        end % onActivePositionPropertyChanged\n        \n    end % event handlers\n    \n    methods( Abstract, Access = protected )\n        \n        redraw( obj )\n        \n    end % abstract template methods\n    \n    methods( Access = protected )\n        \n        function addChild( obj, child )\n            %addChild  Add child\n            %\n            %  c.addChild(d) adds the child d to the container c.\n            \n            % Add to contents\n            obj.Contents_(end+1,:) = child;\n            \n            % Add listeners\n            if isa( child, 'matlab.graphics.axis.Axes' )\n                obj.ActivePositionPropertyListeners{end+1,:} = ...\n                    event.proplistener( child, ...\n                    findprop( child, 'ActivePositionProperty' ), ...\n                    'PostSet', @obj.onActivePositionPropertyChanged );\n            else\n                obj.ActivePositionPropertyListeners{end+1,:} = [];\n            end\n            \n            % Mark as dirty\n            obj.Dirty = true;\n            \n        end % addChild\n        \n        function removeChild( obj, child )\n            %removeChild  Remove child\n            %\n            %  c.removeChild(d) removes the child d from the container c.\n            \n            % Remove from contents\n            contents = obj.Contents_;\n            tf = contents == child;\n            obj.Contents_(tf,:) = [];\n            \n            % Remove listeners\n            obj.ActivePositionPropertyListeners(tf,:) = [];\n            \n            % Mark as dirty\n            obj.Dirty = true;\n            \n        end % removeChild\n        \n        function reparent( obj, oldFigure, newFigure ) %#ok<INUSD>\n            %reparent  Reparent container\n            %\n            %  c.reparent(a,b) reparents the container c from the figure a\n            %  to the figure b.\n            \n        end % reparent\n        \n        function reorder( obj, indices )\n            %reorder  Reorder contents\n            %\n            %  c.reorder(i) reorders the container contents using indices\n            %  i, c.Contents = c.Contents(i).\n            \n            % Reorder contents\n            obj.Contents_ = obj.Contents_(indices,:);\n            \n            % Reorder listeners\n            obj.ActivePositionPropertyListeners = ...\n                obj.ActivePositionPropertyListeners(indices,:);\n            \n            % Mark as dirty\n            obj.Dirty = true;\n            \n        end % reorder\n        \n        function tf = isDrawable( obj )\n            %isDrawable  Test for drawability\n            %\n            %  c.isDrawable() is true if the container c is drawable, and\n            %  false otherwise.  To be drawable, a container must be\n            %  rooted.\n            \n            tf = ~isempty( obj.FigureObserver.Figure );\n            \n        end % isDrawable\n        \n        function track( obj )\n            %track  Track usage\n            \n            persistent TRACKED % single shot\n            if isempty( TRACKED )\n                v = ver( 'layout' );\n                try %#ok<TRYNC>\n                    uix.tracking( 'UA-82270656-2', v(1).Version, class( obj ) )\n                end\n                TRACKED = true;\n            end\n            \n        end % track\n        \n    end % template methods\n    \nend % classdef"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/+mixin/Flex.m",
    "content": "classdef Flex < handle\n    %uix.mixin.Flex  Flex mixin\n    %\n    %  uix.mixin.Flex is a mixin class used by flex containers to provide\n    %  various properties and helper methods.\n    \n    %  Copyright 2016 The MathWorks, Inc.\n    %  $Revision: 1435 $ $Date: 2016-11-17 17:50:34 +0000 (Thu, 17 Nov 2016) $\n    \n    properties( GetAccess = protected, SetAccess = private )\n        Pointer = 'unset' % mouse pointer\n    end\n    \n    properties( Access = private )\n        Figure = gobjects( 0 ); % mouse pointer figure\n        Token = -1 % mouse pointer token\n    end\n    \n    methods\n        \n        function delete( obj )\n            %delete  Destructor\n            \n            % Clean up\n            if ~strcmp( obj.Pointer, 'unset' )\n                obj.unsetPointer()\n            end\n            \n        end % destructor\n        \n    end % structors\n    \n    methods( Access = protected )\n        \n        function setPointer( obj, figure, pointer )\n            %setPointer  Set pointer\n            %\n            %  c.setPointer(f,p) sets the pointer for the figure f to p.\n            \n            % If set, unset\n            if obj.Token ~= -1\n                obj.unsetPointer()\n            end\n            \n            % Set\n            obj.Token = uix.PointerManager.setPointer( figure, pointer );\n            obj.Figure = figure;\n            obj.Pointer = pointer;\n            \n        end % setPointer\n        \n        function unsetPointer( obj )\n            %unsetPointer  Unset pointer\n            %\n            %  c.unsetPointer() undoes the previous pointer set.\n            \n            % Check\n            assert( obj.Token ~= -1, 'uix:InvalidOperation', ...\n                'Pointer is already unset.' )\n            \n            % Unset\n            uix.PointerManager.unsetPointer( obj.Figure, obj.Token );\n            obj.Figure = gobjects( 0 );\n            obj.Pointer = 'unset';\n            obj.Token = -1;\n            \n        end % unsetPointer\n        \n    end % helper methods\n    \nend % classdef"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/+mixin/Panel.m",
    "content": "classdef Panel < uix.mixin.Container\n    %uix.mixin.Panel  Panel mixin\n    %\n    %  uix.mixin.Panel is a mixin class used by panels to provide various\n    %  properties and template methods.\n    \n    %  Copyright 2009-2015 The MathWorks, Inc.\n    %  $Revision: 1435 $ $Date: 2016-11-17 17:50:34 +0000 (Thu, 17 Nov 2016) $\n    \n    properties( Access = public, Dependent, AbortSet )\n        Selection % selected contents\n    end\n    \n    properties( Access = protected )\n        Selection_ = 0 % backing for Selection\n    end\n    \n    properties( Access = protected )\n        G1218142 = false % bug flag\n    end\n    \n    events( NotifyAccess = protected )\n        SelectionChanged % selection changed\n    end\n    \n    methods\n        \n        function value = get.Selection( obj )\n            \n            value = obj.Selection_;\n            \n        end % get.Selection\n        \n        function set.Selection( obj, value )\n            \n            % Check\n            assert( isa( value, 'double' ), 'uix:InvalidPropertyValue', ...\n                'Property ''Selection'' must be of type double.' )\n            assert( isequal( size( value ), [1 1] ), ...\n                'uix:InvalidPropertyValue', ...\n                'Property ''Selection'' must be scalar.' )\n            assert( isreal( value ) && rem( value, 1 ) == 0, ...\n                'uix:InvalidPropertyValue', ...\n                'Property ''Selection'' must be an integer.' )\n            n = numel( obj.Contents_ );\n            if n == 0\n                assert( value == 0, 'uix:InvalidPropertyValue', ...\n                    'Property ''Selection'' must be 0 for a container with no children.' )\n            else\n                assert( value >= 1 && value <= n, 'uix:InvalidPropertyValue', ...\n                    'Property ''Selection'' must be between 1 and the number of children.' )\n            end\n            \n            % Set\n            oldSelection = obj.Selection_;\n            newSelection = value;\n            obj.Selection_ = newSelection;\n            \n            % Show selected child\n            obj.showSelection()\n            \n            % Mark as dirty\n            obj.Dirty = true;\n            \n            % Raise event\n            notify( obj, 'SelectionChanged', ...\n                uix.SelectionData( oldSelection, newSelection ) )\n            \n        end % set.Selection\n        \n    end % accessors\n    \n    methods( Access = protected )\n        \n        function addChild( obj, child )\n            \n            % Check for bug\n            if verLessThan( 'MATLAB', '8.5' ) && strcmp( child.Visible, 'off' )\n                obj.G1218142 = true;\n            end\n            \n            % Select new content\n            oldSelection = obj.Selection_;\n            newSelection = numel( obj.Contents_ ) + 1;\n            obj.Selection_ = newSelection;\n            \n            % Call superclass method\n            addChild@uix.mixin.Container( obj, child )\n            \n            % Show selected child\n            obj.showSelection()\n            \n            % Notify selection change\n            obj.notify( 'SelectionChanged', ...\n                uix.SelectionData( oldSelection, newSelection ) )\n            \n        end % addChild\n        \n        function removeChild( obj, child )\n            \n            % Adjust selection if required\n            contents = obj.Contents_;\n            index = find( contents == child );\n            oldSelection = obj.Selection_;\n            if index < oldSelection\n                newSelection = oldSelection - 1;\n            elseif index == oldSelection\n                newSelection = min( oldSelection, numel( contents ) - 1 );\n            else % index > oldSelection\n                newSelection = oldSelection;\n            end\n            obj.Selection_ = newSelection;\n            \n            % Call superclass method\n            removeChild@uix.mixin.Container( obj, child )\n            \n            % Show selected child\n            obj.showSelection()\n            \n            % Notify selection change\n            if oldSelection ~= newSelection\n                obj.notify( 'SelectionChanged', ...\n                    uix.SelectionData( oldSelection, newSelection ) )\n            end\n            \n        end % removeChild\n        \n        function reorder( obj, indices )\n            %reorder  Reorder contents\n            %\n            %  c.reorder(i) reorders the container contents using indices\n            %  i, c.Contents = c.Contents(i).\n            \n            % Reorder\n            selection = obj.Selection_;\n            if selection ~= 0\n                obj.Selection_ = find( indices == selection );\n            end\n            \n            % Call superclass method\n            reorder@uix.mixin.Container( obj, indices )\n            \n        end % reorder\n        \n        function showSelection( obj )\n            %showSelection  Show selected child, hide the others\n            %\n            %  c.showSelection() shows the selected child of the container\n            %  c, and hides the others.\n            \n            % Set positions and visibility\n            selection = obj.Selection_;\n            children = obj.Contents_;\n            for ii = 1:numel( children )\n                child = children(ii);\n                if ii == selection\n                    if obj.G1218142\n                        warning( 'uix:G1218142', ...\n                            'Selected child of %s is not visible due to bug G1218142.  The child will become visible at the next redraw.', ...\n                            class( obj ) )\n                        obj.G1218142 = false;\n                    else\n                        child.Visible = 'on';\n                    end\n                    if isa( child, 'matlab.graphics.axis.Axes' )\n                        child.ContentsVisible = 'on';\n                    end\n                else\n                    child.Visible = 'off';\n                    if isa( child, 'matlab.graphics.axis.Axes' )\n                        child.ContentsVisible = 'off';\n                    end\n                    % As a remedy for g1100294, move off-screen too\n                    margin = 1000;\n                    if isa( child, 'matlab.graphics.axis.Axes' ) ...\n                            && strcmp(child.ActivePositionProperty, 'outerposition' )\n                        child.OuterPosition(1) = -child.OuterPosition(3)-margin;\n                    else\n                        child.Position(1) = -child.Position(3)-margin;\n                    end\n                end\n            end\n            \n        end % showSelection\n        \n    end % template methods\n    \nend % classdef"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/Box.m",
    "content": "classdef Box < uix.Container & uix.mixin.Container\n    %uix.Box  Box and grid base class\n    %\n    %  uix.Box is a base class for containers with spacing between\n    %  contents.\n    \n    %  Copyright 2009-2015 The MathWorks, Inc.\n    %  $Revision: 1165 $ $Date: 2015-12-06 03:09:17 -0500 (Sun, 06 Dec 2015) $\n    \n    properties( Access = public, Dependent, AbortSet )\n        Spacing = 0 % space between contents, in pixels\n    end\n    \n    properties( Access = protected )\n        Spacing_ = 0 % backing for Spacing\n    end\n    \n    methods\n        \n        function value = get.Spacing( obj )\n            \n            value = obj.Spacing_;\n            \n        end % get.Spacing\n        \n        function set.Spacing( obj, value )\n            \n            % Check\n            assert( isa( value, 'double' ) && isscalar( value ) && ...\n                isreal( value ) && ~isinf( value ) && ...\n                ~isnan( value ) && value >= 0, ...\n                'uix:InvalidPropertyValue', ...\n                'Property ''Spacing'' must be a non-negative scalar.' )\n            \n            % Set\n            obj.Spacing_ = value;\n            \n            % Mark as dirty\n            obj.Dirty = true;\n            \n        end % set.Spacing\n        \n    end % accessors\n    \nend % classdef"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/ChildEvent.m",
    "content": "classdef( Hidden, Sealed ) ChildEvent < event.EventData\n    %uix.ChildEvent  Event data for child event\n    %\n    %  e = uix.ChildEvent(c) creates event data including the child c.\n    \n    %  Copyright 2009-2015 The MathWorks, Inc.\n    %  $Revision: 1165 $ $Date: 2015-12-06 03:09:17 -0500 (Sun, 06 Dec 2015) $\n    \n    properties( SetAccess = private )\n        Child % child\n    end\n    \n    methods\n        \n        function obj = ChildEvent( child )\n            %uix.ChildEvent  Event data for child event\n            %\n            %  e = uix.ChildEvent(c) creates event data including the child\n            %  c.\n            \n            % Set properties\n            obj.Child = child;\n            \n        end % constructor\n        \n    end % structors\n    \nend % classdef"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/ChildObserver.m",
    "content": "classdef ( Hidden, Sealed ) ChildObserver < handle\n    %uix.ChildObserver  Child observer\n    %\n    %  co = uix.ChildObserver(o) creates a child observer for the graphics\n    %  object o.  A child observer raises events when objects are added to\n    %  and removed from the property Children of o.\n    %\n    %  See also: uix.Node\n    \n    %  Copyright 2009-2016 The MathWorks, Inc.\n    %  $Revision: 1436 $ $Date: 2016-11-17 17:53:29 +0000 (Thu, 17 Nov 2016) $\n    \n    properties( Access = private )\n        Root % root node\n    end\n    \n    events( NotifyAccess = private )\n        ChildAdded % child added\n        ChildRemoved % child removed\n    end\n    \n    methods\n        \n        function obj = ChildObserver( oRoot )\n            %uix.ChildObserver  Child observer\n            %\n            %  co = uix.ChildObserver(o) creates a child observer for the\n            %  graphics object o.  A child observer raises events when\n            %  objects are added to and removed from the property Children\n            %  of o.\n            \n            % Check\n            assert( iscontent( oRoot ) && ...\n                isequal( size( oRoot ), [1 1] ), 'uix.InvalidArgument', ...\n                'Object must be a graphics object.' )\n            \n            % Create root node\n            nRoot = uix.Node( oRoot );\n            childAddedListener = event.listener( oRoot, ...\n                'ObjectChildAdded', ...\n                @(~,e)obj.addChild(nRoot,e.Child) );\n            childAddedListener.Recursive = true;\n            nRoot.addprop( 'ChildAddedListener' );\n            nRoot.ChildAddedListener = childAddedListener;\n            childRemovedListener = event.listener( oRoot, ...\n                'ObjectChildRemoved', ...\n                @(~,e)obj.removeChild(nRoot,e.Child) );\n            childRemovedListener.Recursive = true;\n            nRoot.addprop( 'ChildRemovedListener' );\n            nRoot.ChildRemovedListener = childRemovedListener;\n            \n            % Add children\n            oChildren = hgGetTrueChildren( oRoot );\n            for ii = 1:numel( oChildren )\n                obj.addChild( nRoot, oChildren(ii) )\n            end\n            \n            % Store properties\n            obj.Root = nRoot;\n            \n        end % constructor\n        \n    end % structors\n    \n    methods( Access = private )\n        \n        function addChild( obj, nParent, oChild )\n            %addChild  Add child object to parent node\n            %\n            %  co.addChild(np,oc) adds the child object oc to the parent\n            %  node np, either as part of construction of the child\n            %  observer co, or in response to an ObjectChildAdded event on\n            %  an object of interest to co.  This may lead to ChildAdded\n            %  events being raised on co.\n            \n            % Create child node\n            nChild = uix.Node( oChild );\n            nParent.addChild( nChild )\n            if iscontent( oChild )\n                % Add Internal PreSet property listener\n                internalPreSetListener = event.proplistener( oChild, ...\n                    findprop( oChild, 'Internal' ), 'PreSet', ...\n                    @(~,~)obj.preSetInternal(nChild) );\n                nChild.addprop( 'InternalPreSetListener' );\n                nChild.InternalPreSetListener = internalPreSetListener;\n                % Add Internal PostSet property listener\n                internalPostSetListener = event.proplistener( oChild, ...\n                    findprop( oChild, 'Internal' ), 'PostSet', ...\n                    @(~,~)obj.postSetInternal(nChild) );\n                nChild.addprop( 'InternalPostSetListener' );\n                nChild.InternalPostSetListener = internalPostSetListener;\n            else\n                % Add ObjectChildAdded listener\n                childAddedListener = event.listener( oChild, ...\n                    'ObjectChildAdded', ...\n                    @(~,e)obj.addChild(nChild,e.Child) );\n                nChild.addprop( 'ChildAddedListener' );\n                nChild.ChildAddedListener = childAddedListener;\n                % Add ObjectChildRemoved listener\n                childRemovedListener = event.listener( oChild, ...\n                    'ObjectChildRemoved', ...\n                    @(~,e)obj.removeChild(nChild,e.Child) );\n                nChild.addprop( 'ChildRemovedListener' );\n                nChild.ChildRemovedListener = childRemovedListener;\n            end\n            \n            % Raise ChildAdded event\n            if iscontent( oChild ) && oChild.Internal == false\n                notify( obj, 'ChildAdded', uix.ChildEvent( oChild ) )\n            end\n            \n            % Add grandchildren\n            if ~iscontent( oChild )\n                oGrandchildren = hgGetTrueChildren( oChild );\n                for ii = 1:numel( oGrandchildren )\n                    obj.addChild( nChild, oGrandchildren(ii) )\n                end\n            end\n            \n        end % addChild\n        \n        function removeChild( obj, nParent, oChild )\n            %removeChild  Remove child object from parent node\n            %\n            %  co.removeChild(np,oc) removes the child object oc from the\n            %  parent node np, in response to an ObjectChildRemoved event\n            %  on an object of interest to co.  This may lead to\n            %  ChildRemoved events being raised on co.\n            \n            % Get child node\n            nChildren = nParent.Children;\n            tf = oChild == [nChildren.Object];\n            nChild = nChildren(tf);\n            \n            % Raise ChildRemoved event(s)\n            notifyChildRemoved( nChild )\n            \n            % Delete child node\n            delete( nChild )\n            \n            function notifyChildRemoved( nc )\n                \n                % Process child nodes\n                ngc = nc.Children;\n                for ii = 1:numel( ngc )\n                    notifyChildRemoved( ngc(ii) )\n                end\n                \n                % Process this node\n                oc = nc.Object;\n                if iscontent( oc ) && oc.Internal == false\n                    notify( obj, 'ChildRemoved', uix.ChildEvent( oc ) )\n                end\n                \n            end % notifyChildRemoved\n            \n        end % removeChild\n        \n        function preSetInternal( ~, nChild )\n            %preSetInternal  Perform property PreSet tasks\n            %\n            %  co.preSetInternal(n) caches the previous value of the\n            %  property Internal of the object referenced by the node n, to\n            %  enable PostSet tasks to identify whether the value changed.\n            %  This is necessary since Internal AbortSet is false.\n            \n            oldInternal = nChild.Object.Internal;\n            nChild.addprop( 'OldInternal' );\n            nChild.OldInternal = oldInternal;\n            \n        end % preSetInternal\n        \n        function postSetInternal( obj, nChild )\n            %postSetInternal  Perform property PostSet tasks\n            %\n            %  co.postSetInternal(n) raises a ChildAdded or ChildRemoved\n            %  event on the child observer co in response to a change of\n            %  the value of the property Internal of the object referenced\n            %  by the node n.\n            \n            % Retrieve old and new values\n            oChild = nChild.Object;\n            newInternal = oChild.Internal;\n            oldInternal = nChild.OldInternal;\n            \n            % Clean up node\n            delete( findprop( nChild, 'OldInternal' ) )\n            \n            % Raise event\n            switch newInternal\n                case oldInternal % no change\n                    % no event\n                case true % false to true\n                    notify( obj, 'ChildRemoved', uix.ChildEvent( oChild ) )\n                case false % true to false\n                    notify( obj, 'ChildAdded', uix.ChildEvent( oChild ) )\n            end\n            \n        end % postSetInternal\n        \n    end % event handlers\n    \nend % classdef\n\nfunction tf = iscontent( o )\n%iscontent  True for graphics that can be Contents (and can be Children)\n%\n%  uix.ChildObserver needs to determine which objects can be Contents,\n%  which is equivalent to can be Children if HandleVisibility is 'on' and\n%  Internal is false.  Prior to R2016a, this condition could be checked\n%  using isgraphics.  From R2016a, isgraphics returns true for a wider\n%  range of objects, including some that can never by Contents, e.g.,\n%  JavaCanvas.  Therefore this function checks whether an object is of type\n%  matlab.graphics.internal.GraphicsBaseFunctions, which is what isgraphics\n%  did prior to R2016a.\n\ntf = isa( o, 'matlab.graphics.internal.GraphicsBaseFunctions' ) &&...\n     isprop( o, 'Position' );\n\nend % iscontent"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/Container.m",
    "content": "classdef Container < matlab.ui.container.internal.UIContainer\n    %uix.Container  Container base class\n    %\n    %  uix.Container is base class for containers that extend uicontainer.\n    \n    %  Copyright 2009-2015 The MathWorks, Inc.\n    %  $Revision: 1137 $ $Date: 2015-05-29 21:48:21 +0100 (Fri, 29 May 2015) $\n    \nend % classdef"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/Divider.m",
    "content": "classdef Divider < matlab.mixin.SetGet\n    %uix.Divider  Draggable divider\n    %\n    %  d = uix.Divider() creates a divider.\n    %\n    %  d = uix.Divider(p1,v1,p2,v2,...) creates a divider and sets\n    %  specified property p1 to value v1, etc.\n    \n    %  Copyright 2009-2016 The MathWorks, Inc.\n    %  $Revision: 1436 $ $Date: 2016-11-17 17:53:29 +0000 (Thu, 17 Nov 2016) $\n    \n    properties( Dependent )\n        Parent % parent\n        Units % units [inches|centimeters|characters|normalized|points|pixels]\n        Position % position\n        Visible % visible [on|off]\n        BackgroundColor % background color [RGB]\n        HighlightColor % border highlight color [RGB]\n        ShadowColor % border shadow color [RGB]\n        Orientation % orientation [vertical|horizontal]\n        Markings % markings [pixels]\n    end\n    \n    properties( Access = private )\n        Control % uicontrol\n        BackgroundColor_ = get( 0, 'DefaultUicontrolBackgroundColor' ) % backing for BackgroundColor\n        HighlightColor_ = [1 1 1] % backing for HighlightColor\n        ShadowColor_ = [0.7 0.7 0.7] % backing for ShadowColor\n        Orientation_ = 'vertical' % backing for Orientation\n        Markings_ = zeros( [0 1] ) % backing for Markings\n        SizeChangedListener % listener\n    end\n    \n    methods\n        \n        function obj = Divider( varargin )\n            %uix.Divider  Draggable divider\n            %\n            %  d = uix.Divider() creates a divider.\n            %\n            %  d = uix.Divider(p1,v1,p2,v2,...) creates a dividerand sets\n            %  specified property p1 to value v1, etc.\n            \n            % Create control\n            control = matlab.ui.control.UIControl( ...\n                'Style', 'checkbox', 'Internal', true, ...\n                'Enable', 'inactive', 'DeleteFcn', @obj.onDeleted,...\n                'Tag', 'uix.Divider' );\n            \n            % Store control\n            obj.Control = control;\n            \n            % Set properties\n            if nargin > 0\n                try\n                    assert( rem( nargin, 2 ) == 0, 'uix:InvalidArgument', ...\n                        'Parameters and values must be provided in pairs.' )\n                    set( obj, varargin{:} )\n                catch e\n                    delete( obj )\n                    e.throwAsCaller()\n                end\n            end\n            \n            % Force update\n            obj.update()\n            \n            % Create listener\n            sizeChangedListener = event.listener( control, 'SizeChanged', ...\n                @obj.onSizeChanged );\n            \n            % Store listener\n            obj.SizeChangedListener = sizeChangedListener;\n            \n        end % constructor\n        \n        function delete( obj )\n            %delete  Destructor\n            \n            control = obj.Control;\n            if isgraphics( control ) && strcmp( control.BeingDeleted, 'off' )\n                delete( control )\n            end\n            \n        end % destructor\n        \n    end % structors\n    \n    methods\n        \n        function value = get.Parent( obj )\n            \n            value = obj.Control.Parent;\n            \n        end % get.Parent\n        \n        function set.Parent( obj, value )\n            \n            obj.Control.Parent = value;\n            \n        end % set.Parent\n        \n        function value = get.Units( obj )\n            \n            value = obj.Control.Units;\n            \n        end % get.Units\n        \n        function set.Units( obj, value )\n            \n            obj.Control.Units = value;\n            \n        end % set.Units\n        \n        function value = get.Position( obj )\n            \n            value = obj.Control.Position;\n            \n        end % get.Position\n        \n        function set.Position( obj, value )\n            \n            obj.Control.Position = value;\n            \n        end % set.Position\n        \n        function value = get.Visible( obj )\n            \n            value = obj.Control.Visible;\n            \n        end % get.Visible\n        \n        function set.Visible( obj, value )\n            \n            obj.Control.Visible = value;\n            \n        end % set.Visible\n        \n        function value = get.BackgroundColor( obj )\n            \n            value = obj.BackgroundColor_;\n            \n        end % get.BackgroundColor\n        \n        function set.BackgroundColor( obj, value )\n            \n            % Check\n            assert( isa( value, 'double' ) && ...\n                isequal( size( value ), [1 3] ) && ...\n                all( value >= 0 ) && all( value <= 1 ), ...\n                'uix:InvalidArgument', ...\n                'Property ''BackgroundColor'' must be a valid colorspec.' )\n            \n            % Set\n            obj.BackgroundColor_ = value;\n            \n            % Update\n            obj.update()\n            \n        end % set.BackgroundColor\n        \n        function value = get.HighlightColor( obj )\n            \n            value = obj.HighlightColor_;\n            \n        end % get.HighlightColor\n        \n        function set.HighlightColor( obj, value )\n            \n            % Check\n            assert( isnumeric( value ) && isequal( size( value ), [1 3] ) && ...\n                all( isreal( value ) ) && all( value >= 0 ) && all( value <= 1 ), ...\n                'uix:InvalidPropertyValue', ...\n                'Property ''HighlightColor'' must be an RGB triple.' )\n            \n            % Set\n            obj.HighlightColor_ = value;\n            \n            % Update\n            obj.update()\n            \n        end % set.HighlightColor\n        \n        function value = get.ShadowColor( obj )\n            \n            value = obj.ShadowColor_;\n            \n        end % get.ShadowColor\n        \n        function set.ShadowColor( obj, value )\n            \n            % Check\n            assert( isnumeric( value ) && isequal( size( value ), [1 3] ) && ...\n                all( isreal( value ) ) && all( value >= 0 ) && all( value <= 1 ), ...\n                'uix:InvalidPropertyValue', ...\n                'Property ''ShadowColor'' must be an RGB triple.' )\n            \n            % Set\n            obj.ShadowColor_ = value;\n            \n            % Update\n            obj.update()\n            \n        end % set.ShadowColor\n        \n        function value = get.Orientation( obj )\n            \n            value = obj.Orientation_;\n            \n        end % get.Orientation\n        \n        function set.Orientation( obj, value )\n            \n            % Check\n            assert( ischar( value ) && ismember( value, ...\n                {'horizontal','vertical'} ) )\n            \n            % Set\n            obj.Orientation_ = value;\n            \n            % Update\n            obj.update()\n            \n        end % set.Orientation\n        \n        function value = get.Markings( obj )\n            \n            value = obj.Markings_;\n            \n        end % get.Markings\n        \n        function set.Markings( obj, value )\n            \n            % Check\n            assert( isa( value, 'double' ) && ndims( value ) == 2 && ...\n                size( value, 2 ) == 1 && all( isreal( value ) ) && ...\n                all( ~isinf( value ) ) && all( ~isnan( value ) ) && ...\n                all( value > 0 ), 'uix:InvalidPropertyValue', ...\n                'Property ''Markings'' must be a vector of positive values.' ) %#ok<ISMAT>\n            \n            % Set\n            obj.Markings_ = value;\n            \n            % Update\n            obj.update()\n            \n        end % set.Markings\n        \n    end % accessors\n    \n    methods\n        \n        function tf = isMouseOver( obj, eventData )\n            %isMouseOver  Test for mouse over\n            %\n            %  tf = d.isMouseOver(wmd) tests whether the WindowMouseData\n            %  wmd is consistent with the mouse pointer being over the\n            %  divider d.\n            \n            tf = reshape( [obj.Control] == eventData.HitObject, size( obj ) );\n            \n        end % isMouseOver\n        \n    end % methods\n    \n    methods( Access = private )\n        \n        function onDeleted( obj, ~, ~ )\n            %onDeleted  Event handler\n            \n            % Call destructor\n            obj.delete()\n            \n        end % onDeleted\n        \n        function onSizeChanged( obj, ~, ~ )\n            %onSizeChanged  Event handler\n            \n            % Update\n            obj.update()\n            \n        end % onSizeChanged\n        \n    end % event handlers\n    \n    methods( Access = private )\n        \n        function update( obj )\n            %update  Update divider\n            %\n            %  d.update() updates the divider markings.\n            \n            % Get properties\n            control = obj.Control;\n            position = control.Position;\n            backgroundColor = obj.BackgroundColor;\n            highlightColor = obj.HighlightColor;\n            shadowColor = obj.ShadowColor;\n            orientation = obj.Orientation;\n            markings = obj.Markings;\n            \n            % Assemble mask\n            mask = zeros( floor( position([4 3]) ) - [1 1] ); % initialize\n            switch orientation\n                case 'vertical'\n                    markings(markings < 4) = [];\n                    markings(markings > position(4)-6) = [];\n                    for ii = 1:numel( markings )\n                        marking = markings(ii);\n                        mask(floor( marking ) + [-3 0 3],1:end-1) = 1;\n                        mask(floor( marking ) + [-2 1 4],1:end-1) = 2;\n                    end\n                case 'horizontal'\n                    markings(markings < 4) = [];\n                    markings(markings > position(3)-6) = [];\n                    for ii = 1:numel( markings )\n                        marking = markings(ii);\n                        mask(2:end,floor( marking ) + [-3 0 3]) = 1;\n                        mask(2:end,floor( marking ) + [-2 1 4]) = 2;\n                    end\n            end\n            \n            % Assemble color data\n            cData1 = repmat( backgroundColor(1), size( mask ) );\n            cData1(mask==1) = highlightColor(1);\n            cData1(mask==2) = shadowColor(1);\n            cData2 = repmat( backgroundColor(2), size( mask ) );\n            cData2(mask==1) = highlightColor(2);\n            cData2(mask==2) = shadowColor(2);\n            cData3 = repmat( backgroundColor(3), size( mask ) );\n            cData3(mask==1) = highlightColor(3);\n            cData3(mask==2) = shadowColor(3);\n            cData = cat( 3, cData1, cData2, cData3 );\n            \n            % Set properties\n            control.ForegroundColor = backgroundColor;\n            control.BackgroundColor = backgroundColor;\n            control.CData = cData;\n            \n        end % update\n        \n    end % methods\n    \nend % classdef"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/FigureData.m",
    "content": "classdef ( Hidden, Sealed ) FigureData < event.EventData\n    %uix.FigureData  Event data for FigureChanged on uix.FigureObserver\n    \n    %  Copyright 2014-2015 The MathWorks, Inc.\n    %  $Revision: 1435 $ $Date: 2016-11-17 17:50:34 +0000 (Thu, 17 Nov 2016) $\n    \n    properties( SetAccess = private )\n        OldFigure % old figure\n        NewFigure % new figure\n    end\n    \n    methods( Access = ?uix.FigureObserver )\n        \n        function obj = FigureData( oldFigure, newFigure )\n            %uix.FigureData  Create event data\n            %\n            %  d = uix.FigureData(oldFigure,newFigure)\n            \n            obj.OldFigure = oldFigure;\n            obj.NewFigure = newFigure;\n            \n        end % constructor\n        \n    end % methods\n    \nend % classdef"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/FigureObserver.m",
    "content": "classdef ( Hidden, Sealed ) FigureObserver < handle\n    %uix.FigureObserver  Figure observer\n    %\n    %  A figure observer raises an event FigureChanged when the figure\n    %  ancestor of a subject changes.\n    \n    %  Copyright 2014-2015 The MathWorks, Inc.\n    %  $Revision: 1435 $ $Date: 2016-11-17 17:50:34 +0000 (Thu, 17 Nov 2016) $\n    \n    properties( SetAccess = private )\n        Subject % subject\n        Figure % figure ancestor\n    end\n    \n    properties( Access = private )\n        PreSetListeners % listeners to Parent PreGet\n        PostSetListeners % listeners to Parent PreGet\n        OldFigure = gobjects( 0 ) % previous figure ancestor\n    end\n    \n    events( NotifyAccess = private )\n        FigureChanged\n    end\n    \n    methods\n        \n        function obj = FigureObserver( subject )\n            %uix.FigureObserver  Create figure observer\n            %\n            %  o = uix.FigureObserver(s) creates a figure observer for the\n            %  subject s.\n            \n            % Check\n            validateattributes( subject, {'matlab.graphics.Graphics'}, ...\n                {'scalar'}, '', 'subject' )\n            \n            % Store subject\n            obj.Subject = subject;\n            \n            % Set up object\n            obj.update()\n            \n        end % constructor\n        \n    end % structors\n    \n    methods( Access = private )\n        \n        function update( obj )\n            %update  Update listeners and Figure property\n            \n            % Create fresh listeners\n            obj.PreSetListeners = event.proplistener.empty( [1 0] ); % clear\n            obj.PostSetListeners = event.proplistener.empty( [1 0] ); % clear\n            o = obj.Subject;\n            while ~isempty( o ) && ~isa( o, 'matlab.ui.Figure' )\n                obj.PreSetListeners(end+1) = event.proplistener( o, ...\n                    findprop( o, 'Parent' ), 'PreSet', @obj.onParentPreSet );\n                obj.PostSetListeners(end+1) = event.proplistener( o, ...\n                    findprop( o, 'Parent' ), 'PostSet', @obj.onParentPostSet );\n                o = o.Parent;\n            end\n            \n            % Store figure\n            obj.Figure = o;\n            \n        end % update\n        \n        function onParentPreSet( obj, ~, ~ )\n            %onParentPreSet  Event handler\n            \n            % Store old figure\n            obj.OldFigure = obj.Figure;\n            \n        end % onParentPreSet\n        \n        function onParentPostSet( obj, ~, ~ )\n            %onParentPostSet  Event handler\n            \n            % Update object\n            obj.update()\n            \n            % Raise event\n            oldFigure = obj.OldFigure;\n            newFigure = obj.Figure;\n            if ~isequal( oldFigure, newFigure )\n                notify( obj, 'FigureChanged', ...\n                    uix.FigureData( oldFigure, newFigure ) )\n            end\n            \n            % Clear old figure\n            obj.OldFigure = gobjects( 0 );\n            \n        end % onParentPostSet\n        \n    end % private methods\n    \nend % classdef"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/Grid.m",
    "content": "classdef Grid < uix.Box\n    %uix.Grid  Grid\n    %\n    %  b = uix.Grid(p1,v1,p2,v2,...) constructs a grid and sets parameter\n    %  p1 to value v1, etc.\n    %\n    %  A grid lays out contents from top to bottom and left to right.\n    %\n    %  See also: uix.HBox, uix.VBox, uix.GridFlex\n    \n    %  Copyright 2009-2016 The MathWorks, Inc.\n    %  $Revision: 1436 $ $Date: 2016-11-17 17:53:29 +0000 (Thu, 17 Nov 2016) $\n    \n    properties( Access = public, Dependent, AbortSet )\n        Widths % widths of contents, in pixels and/or weights\n        MinimumWidths % minimum widths of contents, in pixels\n        Heights % heights of contents, in pixels and/or weights\n        MinimumHeights % minimum heights of contents, in pixels\n    end\n    \n    properties( Access = protected )\n        Widths_ = zeros( [0 1] ) % backing for Widths\n        MinimumWidths_ = zeros( [0 1] ) % backing for MinimumWidths\n        Heights_ = zeros( [0 1] ) % backing for Heights\n        MinimumHeights_ = zeros( [0 1] ) % backing for MinimumHeights\n    end\n    \n    methods\n        \n        function obj = Grid( varargin )\n            %uix.Grid  Grid constructor\n            %\n            %  b = uix.Grid() constructs a grid.\n            %\n            %  b = uix.Grid(p1,v1,p2,v2,...) sets parameter p1 to value v1,\n            %  etc.\n            \n            % Set properties\n            if nargin > 0\n                try\n                    assert( rem( nargin, 2 ) == 0, 'uix:InvalidArgument', ...\n                        'Parameters and values must be provided in pairs.' )\n                    set( obj, varargin{:} )\n                catch e\n                    delete( obj )\n                    e.throwAsCaller()\n                end\n            end\n            \n        end % constructor\n        \n    end % structors\n    \n    methods\n        \n        function value = get.Widths( obj )\n            \n            value = obj.Widths_;\n            \n        end % get.Widths\n        \n        function set.Widths( obj, value )\n            \n            % For those who can't tell a column from a row...\n            if isrow( value )\n                value = transpose( value );\n            end\n            \n            % Check\n            assert( isa( value, 'double' ), 'uix:InvalidPropertyValue', ...\n                'Property ''Widths'' must be of type double.' )\n            assert( all( isreal( value ) ) && ~any( isinf( value ) ) && ...\n                ~any( isnan( value ) ), 'uix:InvalidPropertyValue', ...\n                'Elements of property ''Widths'' must be real and finite.' )\n            n = numel( obj.Contents_ );\n            b = numel( obj.Widths_ );\n            q = numel( obj.Heights_ );\n            c = numel( value );\n            r = ceil( n / c );\n            if c < min( [1 n] )\n                error( 'uix:InvalidPropertyValue' , ...\n                    'Property ''Widths'' must be non-empty for non-empty contents.' )\n            elseif ceil( n / r ) < c\n                error( 'uix:InvalidPropertyValue' , ...\n                    'Size of property ''Widths'' must not lead to empty columns.' )\n            elseif c > n\n                error( 'uix:InvalidPropertyValue' , ...\n                    'Size of property ''Widths'' must be no larger than size of contents.' )\n            end\n            \n            % Set\n            obj.Widths_ = value;\n            if c < b % number of columns decreasing\n                obj.MinimumWidths_(c+1:end,:) = [];\n                if r > q % number of rows increasing\n                    obj.Heights_(end+1:r,:) = -1;\n                    obj.MinimumHeights_(end+1:r,:) = 1;\n                end\n            elseif c > b % number of columns increasing\n                obj.MinimumWidths_(end+1:c,:) = -1;\n                if r < q % number of rows decreasing\n                    obj.Heights_(r+1:end,:) = [];\n                    obj.MinimumHeights_(r+1:end,:) = [];\n                end\n            end\n            \n            % Mark as dirty\n            obj.Dirty = true;\n            \n        end % set.Widths\n        \n        function value = get.MinimumWidths( obj )\n            \n            value = obj.MinimumWidths_;\n            \n        end % get.MinimumWidths\n        \n        function set.MinimumWidths( obj, value )\n            \n            % For those who can't tell a column from a row...\n            if isrow( value )\n                value = transpose( value );\n            end\n            \n            % Check\n            assert( isa( value, 'double' ), 'uix:InvalidPropertyValue', ...\n                'Property ''MinimumWidths'' must be of type double.' )\n            assert( all( isreal( value ) ) && ~any( isinf( value ) ) && ...\n                all( value >= 0 ), 'uix:InvalidPropertyValue', ...\n                'Elements of property ''MinimumWidths'' must be non-negative.' )\n            assert( isequal( size( value ), size( obj.Widths_ ) ), ...\n                'uix:InvalidPropertyValue', ...\n                'Size of property ''MinimumWidths'' must match size of contents.' )\n            \n            % Set\n            obj.MinimumWidths_ = value;\n            \n            % Mark as dirty\n            obj.Dirty = true;\n            \n        end % set.MinimumWidths\n        \n        function value = get.Heights( obj )\n            \n            value = obj.Heights_;\n            \n        end % get.Heights\n        \n        function set.Heights( obj, value )\n            \n            % For those who can't tell a column from a row...\n            if isrow( value )\n                value = transpose( value );\n            end\n            \n            % Check\n            assert( isa( value, 'double' ), 'uix:InvalidPropertyValue', ...\n                'Property ''Heights'' must be of type double.' )\n            assert( all( isreal( value ) ) && ~any( isinf( value ) ) && ...\n                ~any( isnan( value ) ), 'uix:InvalidPropertyValue', ...\n                'Elements of property ''Heights'' must be real and finite.' )\n            n = numel( obj.Contents_ );\n            b = numel( obj.Widths_ );\n            q = numel( obj.Heights_ );\n            r = numel( value );\n            c = ceil( n / r );\n            if r < min( [1 n] )\n                error( 'uix:InvalidPropertyValue' , ...\n                    'Property ''Heights'' must be non-empty for non-empty contents.' )\n            elseif r > n\n                error( 'uix:InvalidPropertyValue' , ...\n                    'Size of property ''Heights'' must be no larger than size of contents.' )\n            end\n            \n            % Set\n            obj.Heights_ = value;\n            if r < q % number of rows decreasing\n                obj.MinimumHeights_(r+1:end,:) = [];\n                if c > b % number of columns increasing\n                    obj.Widths_(end+1:c,:) = -1;\n                    obj.MinimumWidths_(end+1:c,:) = 1;\n                end\n            elseif r > q % number of rows increasing\n                obj.MinimumHeights_(end+1:r,:) = 1;\n                if c < b % number of columns decreasing\n                    obj.Widths_(c+1:end,:) = [];\n                    obj.MinimumWidths_(c+1:end,:) = [];\n                end\n            end\n            \n            % Mark as dirty\n            obj.Dirty = true;\n            \n        end % set.Heights\n        \n        function value = get.MinimumHeights( obj )\n            \n            value = obj.MinimumHeights_;\n            \n        end % get.MinimumHeights\n        \n        function set.MinimumHeights( obj, value )\n            \n            % For those who can't tell a column from a row...\n            if isrow( value )\n                value = transpose( value );\n            end\n            \n            % Check\n            assert( isa( value, 'double' ), 'uix:InvalidPropertyValue', ...\n                'Property ''MinimumHeights'' must be of type double.' )\n            assert( all( isreal( value ) ) && ~any( isinf( value ) ) && ...\n                all( value >= 0 ), 'uix:InvalidPropertyValue', ...\n                'Elements of property ''MinimumHeights'' must be non-negative.' )\n            assert( isequal( size( value ), size( obj.Heights_ ) ), ...\n                'uix:InvalidPropertyValue', ...\n                'Size of property ''MinimumHeights'' must match size of contents.' )\n            \n            % Set\n            obj.MinimumHeights_ = value;\n            \n            % Mark as dirty\n            obj.Dirty = true;\n            \n        end % set.MinimumHeights\n        \n    end % accessors\n    \n    methods( Access = protected )\n        \n        function redraw( obj )\n            %redraw  Redraw\n            %\n            %  c.redraw() redraws the container c.\n            \n            % Compute positions\n            bounds = hgconvertunits( ancestor( obj, 'figure' ), ...\n                [0 0 1 1], 'normalized', 'pixels', obj );\n            widths = obj.Widths_;\n            minimumWidths = obj.MinimumWidths_;\n            heights = obj.Heights_;\n            minimumHeights = obj.MinimumHeights_;\n            padding = obj.Padding_;\n            spacing = obj.Spacing_;\n            c = numel( widths );\n            r = numel( heights );\n            n = numel( obj.Contents_ );\n            xSizes = uix.calcPixelSizes( bounds(3), widths, ...\n                minimumWidths, padding, spacing );\n            xPositions = [cumsum( [0; xSizes(1:end-1,:)] ) + padding + ...\n                spacing * transpose( 0:c-1 ) + 1, xSizes];\n            ySizes = uix.calcPixelSizes( bounds(4), heights, ...\n                minimumHeights, padding, spacing );\n            yPositions = [bounds(4) - cumsum( ySizes ) - padding - ...\n                spacing * transpose( 0:r-1 ) + 1, ySizes];\n            [iy, ix] = ind2sub( [r c], transpose( 1:n ) );\n            positions = [xPositions(ix,1), yPositions(iy,1), ...\n                xPositions(ix,2), yPositions(iy,2)];\n            \n            % Set positions\n            children = obj.Contents_;\n            for ii = 1:numel( children )\n                uix.setPosition( children(ii), positions(ii,:), 'pixels' )\n            end\n            \n        end % redraw\n        \n        function addChild( obj, child )\n            %addChild  Add child\n            %\n            %  c.addChild(d) adds the child d to the container c.\n            \n            % Add column and even a row if necessary\n            n = numel( obj.Contents_ );\n            c = numel( obj.Widths_ );\n            r = numel( obj.Heights_ );\n            if n == 0\n                obj.Widths_(end+1,:) = -1;\n                obj.MinimumWidths_(end+1,:) = 1;\n                obj.Heights_(end+1,:) = -1;\n                obj.MinimumHeights_(end+1,:) = 1;\n            elseif ceil( (n+1)/r ) > c\n                obj.Widths_(end+1,:) = -1;\n                obj.MinimumWidths_(end+1,:) = 1;\n            end\n            \n            % Call superclass method\n            addChild@uix.Box( obj, child )\n            \n        end % addChild\n        \n        function removeChild( obj, child )\n            %removeChild  Remove child\n            %\n            %  c.removeChild(d) removes the child d from the container c.\n            \n            % Remove column and even row if necessary\n            n = numel( obj.Contents_ );\n            c = numel( obj.Widths_ );\n            r = numel( obj.Heights_ );\n            if n == 1\n                obj.Widths_(end,:) = [];\n                obj.MinimumWidths_(end,:) = [];\n                obj.Heights_(end,:) = [];\n                obj.MinimumHeights_(end,:) = [];\n            elseif c == 1\n                obj.Heights_(end,:) = [];\n                obj.MinimumHeights_(end,:) = [];\n            elseif ceil( (n-1)/r ) < c\n                obj.Widths_(end,:) = [];\n                obj.MinimumWidths_(end,:) = [];\n            end\n            \n            % Call superclass method\n            removeChild@uix.Box( obj, child )\n            \n        end % removeChild\n        \n    end % template methods\n    \nend % classdef"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/GridFlex.m",
    "content": "classdef GridFlex < uix.Grid & uix.mixin.Flex\n    %uix.GridFlex  Flexible grid\n    %\n    %  b = uix.GridFlex(p1,v1,p2,v2,...) constructs a flexible grid and\n    %  sets parameter p1 to value v1, etc.\n    %\n    %  A grid lays out contents from top to bottom and left to right.\n    %  Users can resize contents by dragging the dividers.\n    %\n    %  See also: uix.HBoxFlex, uix.VBoxFlex, uix.Grid\n    \n    %  Copyright 2009-2016 The MathWorks, Inc.\n    %  $Revision: 1436 $ $Date: 2016-11-17 17:53:29 +0000 (Thu, 17 Nov 2016) $\n    \n    properties( Access = public, Dependent, AbortSet )\n        DividerMarkings % divider markings [on|off]\n    end\n    \n    properties( Access = private )\n        RowDividers = uix.Divider.empty( [0 1] )\n        ColumnDividers = uix.Divider.empty( [0 1] )\n        FrontDivider % front divider\n        DividerMarkings_ = 'on' % backing for DividerMarkings\n        MousePressListener = event.listener.empty( [0 0] ) % mouse press listener\n        MouseReleaseListener = event.listener.empty( [0 0] ) % mouse release listener\n        MouseMotionListener = event.listener.empty( [0 0] ) % mouse motion listener\n        ActiveDivider = 0 % active divider index\n        ActiveDividerPosition = [NaN NaN NaN NaN] % active divider position\n        MousePressLocation = [NaN NaN] % mouse press location\n        BackgroundColorListener % background color listener\n    end\n    \n    methods\n        \n        function obj = GridFlex( varargin )\n            %uix.GridFlex  Flexible grid constructor\n            %\n            %  b = uix.GridFlex() constructs a flexible grid.\n            %\n            %  b = uix.GridFlex(p1,v1,p2,v2,...) sets parameter p1 to value\n            %  v1, etc.\n            \n            % Create front divider\n            frontDivider = uix.Divider( 'Parent', obj, ...\n                'Orientation', 'vertical', ...\n                'BackgroundColor', obj.BackgroundColor * 0.75, ...\n                'Visible', 'off' );\n            \n            % Create listeners\n            backgroundColorListener = event.proplistener( obj, ...\n                findprop( obj, 'BackgroundColor' ), 'PostSet', ...\n                @obj.onBackgroundColorChange );\n            \n            % Store properties\n            obj.FrontDivider = frontDivider;\n            obj.BackgroundColorListener = backgroundColorListener;\n            \n            % Set properties\n            if nargin > 0\n                try\n                    assert( rem( nargin, 2 ) == 0, 'uix:InvalidArgument', ...\n                        'Parameters and values must be provided in pairs.' )\n                    set( obj, varargin{:} )\n                catch e\n                    delete( obj )\n                    e.throwAsCaller()\n                end\n            end\n            \n        end % constructor\n        \n    end % structors\n    \n    methods\n        \n        function value = get.DividerMarkings( obj )\n            \n            value = obj.DividerMarkings_;\n            \n        end % get.DividerMarkings\n        \n        function set.DividerMarkings( obj, value )\n            \n            % Check\n            assert( ischar( value ) && any( strcmp( value, {'on','off'} ) ), ...\n                'uix:InvalidArgument', ...\n                'Property ''DividerMarkings'' must be ''on'' or ''off'.' )\n            \n            % Set\n            obj.DividerMarkings_ = value;\n            \n            % Mark as dirty\n            obj.Dirty = true;\n            \n        end % set.DividerMarkings\n        \n    end % accessors\n    \n    methods( Access = protected )\n        \n        function onMousePress( obj, source, eventData )\n            %onMousePress  Handler for WindowMousePress events\n            \n            % Check whether mouse is over a divider\n            locr = find( obj.RowDividers.isMouseOver( eventData ) );\n            locc = find( obj.ColumnDividers.isMouseOver( eventData ) );\n            if ~isempty( locr )\n                loc = locr;\n                divider = obj.RowDividers(locr);\n            elseif ~isempty( locc )\n                loc = -locc;\n                divider = obj.ColumnDividers(locc);\n            else\n                return\n            end\n            \n            % Capture state at button down\n            obj.ActiveDivider = loc;\n            obj.ActiveDividerPosition = divider.Position;\n            root = groot();\n            obj.MousePressLocation = root.PointerLocation;\n            \n            % Make sure the pointer is appropriate\n            obj.updateMousePointer( source, eventData );\n            \n            % Activate divider\n            frontDivider = obj.FrontDivider;\n            frontDivider.Position = divider.Position;\n            frontDivider.Orientation = divider.Orientation;\n            divider.Visible = 'off';\n            frontDivider.Parent = [];\n            frontDivider.Parent = obj;\n            frontDivider.Visible = 'on';\n            \n        end % onMousePress\n        \n        function onMouseRelease( obj, ~, ~ )\n            %onMousePress  Handler for WindowMouseRelease events\n            \n            % Compute new positions\n            loc = obj.ActiveDivider;\n            if loc > 0\n                root = groot();\n                delta = root.PointerLocation(2) - obj.MousePressLocation(2);\n                ih = loc;\n                jh = loc + 1;\n                ic = loc;\n                jc = loc + 1;\n                divider = obj.RowDividers(loc);\n                contents = obj.Contents_;\n                oldPixelHeights = [contents(ic).Position(4); contents(jc).Position(4)];\n                minimumHeights = obj.MinimumHeights_(ih:jh,:);\n                if delta < 0 % limit to minimum distance from lower neighbor\n                    delta = max( delta, minimumHeights(2) - oldPixelHeights(2) );\n                else % limit to minimum distance from upper neighbor\n                    delta = min( delta, oldPixelHeights(1) - minimumHeights(1) );\n                end\n                oldHeights = obj.Heights_(loc:loc+1);\n                newPixelHeights = oldPixelHeights - delta * [1;-1];\n                if oldHeights(1) < 0 && oldHeights(2) < 0 % weight, weight\n                    newHeights = oldHeights .* newPixelHeights ./ oldPixelHeights;\n                elseif oldHeights(1) < 0 && oldHeights(2) >= 0 % weight, pixels\n                    newHeights = [oldHeights(1) * newPixelHeights(1) / ...\n                        oldPixelHeights(1); newPixelHeights(2)];\n                elseif oldHeights(1) >= 0 && oldHeights(2) < 0 % pixels, weight\n                    newHeights = [newPixelHeights(1); oldHeights(2) * ...\n                        newPixelHeights(2) / oldPixelHeights(2)];\n                else % sizes(1) >= 0 && sizes(2) >= 0 % pixels, pixels\n                    newHeights = newPixelHeights;\n                end\n                obj.Heights_(loc:loc+1) = newHeights;\n            elseif loc < 0\n                root = groot();\n                delta = root.PointerLocation(1) - obj.MousePressLocation(1);\n                iw = -loc;\n                jw = -loc + 1;\n                r = numel( obj.Heights_ );\n                ic = r * (-loc-1) + 1;\n                jc = r * -loc + 1;\n                divider = obj.ColumnDividers(iw);\n                contents = obj.Contents_;\n                oldPixelWidths = [contents(ic).Position(3); contents(jc).Position(3)];\n                minimumWidths = obj.MinimumWidths_(iw:jw,:);\n                if delta < 0 % limit to minimum distance from left neighbor\n                    delta = max( delta, minimumWidths(1) - oldPixelWidths(1) );\n                else % limit to minimum distance from right neighbor\n                    delta = min( delta, oldPixelWidths(2) - minimumWidths(2) );\n                end\n                oldWidths = obj.Widths_(iw:jw);\n                newPixelWidths = oldPixelWidths + delta * [1;-1];\n                if oldWidths(1) < 0 && oldWidths(2) < 0 % weight, weight\n                    newWidths = oldWidths .* newPixelWidths ./ oldPixelWidths;\n                elseif oldWidths(1) < 0 && oldWidths(2) >= 0 % weight, pixels\n                    newWidths = [oldWidths(1) * newPixelWidths(1) / ...\n                        oldPixelWidths(1); newPixelWidths(2)];\n                elseif oldWidths(1) >= 0 && oldWidths(2) < 0 % pixels, weight\n                    newWidths = [newPixelWidths(1); oldWidths(2) * ...\n                        newPixelWidths(2) / oldPixelWidths(2)];\n                else % sizes(1) >= 0 && sizes(2) >= 0 % pixels, pixels\n                    newWidths = newPixelWidths;\n                end\n                obj.Widths_(iw:jw) = newWidths;\n            else\n                return\n            end\n            \n            % Deactivate divider\n            obj.FrontDivider.Visible = 'off';\n            divider.Visible = 'on';\n            \n            % Reset state at button down\n            obj.ActiveDivider = 0;\n            obj.ActiveDividerPosition = [NaN NaN NaN NaN];\n            obj.MousePressLocation = [NaN NaN];\n            \n            % Mark as dirty\n            obj.Dirty = true;\n            \n        end % onMouseRelease\n        \n        function onMouseMotion( obj, source, eventData )\n            %onMouseMotion  Handler for WindowMouseMotion events\n            \n            loc = obj.ActiveDivider;\n            if loc == 0 % hovering, update pointer\n                obj.updateMousePointer( source, eventData );\n            elseif loc > 0 % dragging row divider\n                root = groot();\n                delta = root.PointerLocation(2) - obj.MousePressLocation(2);\n                ih = loc;\n                jh = loc + 1;\n                ic = loc;\n                jc = loc + 1;\n                contents = obj.Contents_;\n                oldPixelHeights = [contents(ic).Position(4); contents(jc).Position(4)];\n                minimumHeights = obj.MinimumHeights_(ih:jh,:);\n                if delta < 0 % limit to minimum distance from lower neighbor\n                    delta = max( delta, minimumHeights(2) - oldPixelHeights(2) );\n                else % limit to minimum distance from upper neighbor\n                    delta = min( delta, oldPixelHeights(1) - minimumHeights(1) );\n                end\n                obj.FrontDivider.Position = ...\n                    obj.ActiveDividerPosition + [0 delta 0 0];\n            else % loc < 0, dragging column divider\n                root = groot();\n                delta = root.PointerLocation(1) - obj.MousePressLocation(1);\n                iw = -loc;\n                jw = -loc + 1;\n                r = numel( obj.Heights_ );\n                ic = r * (-loc-1) + 1;\n                jc = r * -loc + 1;\n                contents = obj.Contents_;\n                oldPixelWidths = [contents(ic).Position(3); contents(jc).Position(3)];\n                minimumWidths = obj.MinimumWidths_(iw:jw,:);\n                if delta < 0 % limit to minimum distance from left neighbor\n                    delta = max( delta, minimumWidths(1) - oldPixelWidths(1) );\n                else % limit to minimum distance from right neighbor\n                    delta = min( delta, oldPixelWidths(2) - minimumWidths(2) );\n                end\n                obj.FrontDivider.Position = ...\n                    obj.ActiveDividerPosition + [delta 0 0 0];\n            end\n            \n        end % onMouseMotion\n        \n        function onBackgroundColorChange( obj, ~, ~ )\n            %onBackgroundColorChange  Handler for BackgroundColor changes\n            \n            backgroundColor = obj.BackgroundColor;\n            highlightColor = min( [backgroundColor / 0.75; 1 1 1] );\n            shadowColor = max( [backgroundColor * 0.75; 0 0 0] );\n            rowDividers = obj.RowDividers;\n            for ii = 1:numel( rowDividers )\n                rowDivider = rowDividers(ii);\n                rowDivider.BackgroundColor = backgroundColor;\n                rowDivider.HighlightColor = highlightColor;\n                rowDivider.ShadowColor = shadowColor;\n            end\n            columnDividers = obj.ColumnDividers;\n            for jj = 1:numel( columnDividers )\n                columnDivider = columnDividers(jj);\n                columnDivider.BackgroundColor = backgroundColor;\n                columnDivider.HighlightColor = highlightColor;\n                columnDivider.ShadowColor = shadowColor;\n            end\n            frontDivider = obj.FrontDivider;\n            frontDivider.BackgroundColor = shadowColor;\n            \n        end % onBackgroundColorChange\n        \n    end % event handlers\n    \n    methods( Access = protected )\n        \n        function redraw( obj )\n            %redraw  Redraw contents\n            %\n            %  c.redraw() redraws the container c.\n            \n            % Call superclass method\n            redraw@uix.Grid( obj )\n            \n            % Create or destroy column dividers\n            b = numel( obj.ColumnDividers ); % current number of dividers\n            c = max( [numel( obj.Widths_ )-1 0] ); % required number of dividers\n            if b < c % create\n                for ii = b+1:c\n                    columnDivider = uix.Divider( 'Parent', obj, ...\n                        'Orientation', 'vertical', ...\n                        'BackgroundColor', obj.BackgroundColor );\n                    obj.ColumnDividers(ii,:) = columnDivider;\n                end\n            elseif b > c % destroy\n                % Destroy dividers\n                delete( obj.ColumnDividers(c+1:b,:) )\n                obj.ColumnDividers(c+1:b,:) = [];\n                % Update pointer\n                if c == 0 && strcmp( obj.Pointer, 'left' )\n                    obj.unsetPointer()\n                end\n            end\n            \n            % Create or destroy row dividers\n            q = numel( obj.RowDividers ); % current number of dividers\n            r = max( [numel( obj.Heights_ )-1 0] ); % required number of dividers\n            if q < r % create\n                for ii = q+1:r\n                    columnDivider = uix.Divider( 'Parent', obj, ...\n                        'Orientation', 'horizontal', ...\n                        'BackgroundColor', obj.BackgroundColor );\n                    obj.RowDividers(ii,:) = columnDivider;\n                end\n                % Bring front divider to the front\n                frontDivider = obj.FrontDivider;\n                frontDivider.Parent = [];\n                frontDivider.Parent = obj;\n            elseif q > r % destroy\n                % Destroy dividers\n                delete( obj.RowDividers(r+1:q,:) )\n                obj.RowDividers(r+1:q,:) = [];\n                % Update pointer\n                if r == 0 && strcmp( obj.Pointer, 'top' )\n                    obj.unsetPointer()\n                end\n            end\n            \n            % Compute container bounds\n            bounds = hgconvertunits( ancestor( obj, 'figure' ), ...\n                [0 0 1 1], 'normalized', 'pixels', obj );\n            \n            % Retrieve size properties\n            widths = obj.Widths_;\n            minimumWidths = obj.MinimumWidths_;\n            heights = obj.Heights_;\n            minimumHeights = obj.MinimumHeights_;\n            padding = obj.Padding_;\n            spacing = obj.Spacing_;\n            \n            % Compute row divider positions\n            xRowPositions = [padding + 1, max( bounds(3) - 2 * padding, 1 )];\n            xRowPositions = repmat( xRowPositions, [r 1] );\n            yRowSizes = uix.calcPixelSizes( bounds(4), heights, ...\n                minimumHeights, padding, spacing );\n            yRowPositions = [bounds(4) - cumsum( yRowSizes(1:r,:) ) - padding - ...\n                spacing * transpose( 1:r ) + 1, repmat( spacing, [r 1] )];\n            rowPositions = [xRowPositions(:,1), yRowPositions(:,1), ...\n                xRowPositions(:,2), yRowPositions(:,2)];\n            \n            % Compute column divider positions\n            xColumnSizes = uix.calcPixelSizes( bounds(3), widths, ...\n                minimumWidths, padding, spacing );\n            xColumnPositions = [cumsum( xColumnSizes(1:c,:) ) + padding + ...\n                spacing * transpose( 0:c-1 ) + 1, repmat( spacing, [c 1] )];\n            yColumnPositions = [padding + 1, max( bounds(4) - 2 * padding, 1 )];\n            yColumnPositions = repmat( yColumnPositions, [c 1] );\n            columnPositions = [xColumnPositions(:,1), yColumnPositions(:,1), ...\n                xColumnPositions(:,2), yColumnPositions(:,2)];\n            \n            % Position row dividers\n            for ii = 1:r\n                rowDivider = obj.RowDividers(ii);\n                rowDivider.Position = rowPositions(ii,:);\n                switch obj.DividerMarkings_\n                    case 'on'\n                        rowDivider.Markings = cumsum( xColumnSizes ) + ...\n                            spacing * transpose( 0:c ) - xColumnSizes / 2;\n                    case 'off'\n                        rowDivider.Markings = zeros( [0 1] );\n                end\n            end\n            \n            % Position column dividers\n            for ii = 1:c\n                columnDivider = obj.ColumnDividers(ii);\n                columnDivider.Position = columnPositions(ii,:);\n                switch obj.DividerMarkings_\n                    case 'on'\n                        columnDivider.Markings = cumsum( yRowSizes ) + ...\n                            spacing * transpose( 0:r ) - yRowSizes / 2;\n                    case 'off'\n                        columnDivider.Markings = zeros( [0 1] );\n                end\n            end\n            \n        end % redraw\n        \n        function reparent( obj, oldFigure, newFigure )\n            %reparent  Reparent container\n            %\n            %  c.reparent(a,b) reparents the container c from the figure a\n            %  to the figure b.\n            \n            % Update listeners\n            if isempty( newFigure )\n                mousePressListener = event.listener.empty( [0 0] );\n                mouseReleaseListener = event.listener.empty( [0 0] );\n                mouseMotionListener = event.listener.empty( [0 0] );\n            else\n                mousePressListener = event.listener( newFigure, ...\n                    'WindowMousePress', @obj.onMousePress );\n                mouseReleaseListener = event.listener( newFigure, ...\n                    'WindowMouseRelease', @obj.onMouseRelease );\n                mouseMotionListener = event.listener( newFigure, ...\n                    'WindowMouseMotion', @obj.onMouseMotion );\n            end\n            obj.MousePressListener = mousePressListener;\n            obj.MouseReleaseListener = mouseReleaseListener;\n            obj.MouseMotionListener = mouseMotionListener;\n            \n            % Call superclass method\n            reparent@uix.Grid( obj, oldFigure, newFigure )\n            \n            % Update pointer\n            if ~isempty( oldFigure ) && ~strcmp( obj.Pointer, 'unset' )\n                obj.unsetPointer()\n            end\n            \n        end % reparent\n        \n    end % template methods\n    \n    methods( Access = protected )\n        \n        function updateMousePointer ( obj, source, eventData  )\n            \n            oldPointer = obj.Pointer;\n            if any( obj.RowDividers.isMouseOver( eventData ) )\n                newPointer = 'top';\n            elseif any( obj.ColumnDividers.isMouseOver( eventData ) )\n                newPointer = 'left';\n            else\n                newPointer = 'unset';\n            end\n            switch newPointer\n                case oldPointer % no change\n                    % do nothing\n                case 'unset' % change, unset\n                    obj.unsetPointer()\n                otherwise % change, set\n                    obj.setPointer( source, newPointer )\n            end\n            \n        end % updateMousePointer\n        \n    end % helpers methods\n    \nend % classdef"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/HBox.m",
    "content": "classdef HBox < uix.Box\n    %uix.HBox  Horizontal box\n    %\n    %  b = uix.HBox(p1,v1,p2,v2,...) constructs a horizontal box and sets\n    %  parameter p1 to value v1, etc.\n    %\n    %  A horizontal box lays out contents from left to right.\n    %\n    %  See also: uix.VBox, uix.Grid, uix.HButtonBox, uix.HBoxFlex\n    \n    %  Copyright 2009-2016 The MathWorks, Inc.\n    %  $Revision: 1436 $ $Date: 2016-11-17 17:53:29 +0000 (Thu, 17 Nov 2016) $\n    \n    properties( Access = public, Dependent, AbortSet )\n        Widths % widths of contents, in pixels and/or weights\n        MinimumWidths % minimum widths of contents, in pixels\n    end\n    \n    properties( Access = protected )\n        Widths_ = zeros( [0 1] ) % backing for Widths\n        MinimumWidths_ = zeros( [0 1] ) % backing for MinimumWidths\n    end\n    \n    methods\n        \n        function obj = HBox( varargin )\n            %uix.HBox  Horizontal box constructor\n            %\n            %  b = uix.HBox() constructs a horizontal box.\n            %\n            %  b = uix.HBox(p1,v1,p2,v2,...) sets parameter p1 to value v1,\n            %  etc.\n            \n            % Set properties\n            if nargin > 0\n                try\n                    assert( rem( nargin, 2 ) == 0, 'uix:InvalidArgument', ...\n                        'Parameters and values must be provided in pairs.' )\n                    set( obj, varargin{:} )\n                catch e\n                    delete( obj )\n                    e.throwAsCaller()\n                end\n            end\n            \n        end % constructor\n        \n    end % structors\n    \n    methods\n        \n        function value = get.Widths( obj )\n            \n            value = obj.Widths_;\n            \n        end % get.Widths\n        \n        function set.Widths( obj, value )\n            \n            % For those who can't tell a column from a row...\n            if isrow( value )\n                value = transpose( value );\n            end\n            \n            % Check\n            assert( isa( value, 'double' ), 'uix:InvalidPropertyValue', ...\n                'Property ''Widths'' must be of type double.' )\n            assert( all( isreal( value ) ) && ~any( isinf( value ) ) && ...\n                ~any( isnan( value ) ), 'uix:InvalidPropertyValue', ...\n                'Elements of property ''Widths'' must be real and finite.' )\n            assert( isequal( size( value ), size( obj.Contents_ ) ), ...\n                'uix:InvalidPropertyValue', ...\n                'Size of property ''Widths'' must match size of contents.' )\n            \n            % Set\n            obj.Widths_ = value;\n            \n            % Mark as dirty\n            obj.Dirty = true;\n            \n        end % set.Widths\n        \n        function value = get.MinimumWidths( obj )\n            \n            value = obj.MinimumWidths_;\n            \n        end % get.MinimumWidths\n        \n        function set.MinimumWidths( obj, value )\n            \n            % For those who can't tell a column from a row...\n            if isrow( value )\n                value = transpose( value );\n            end\n            \n            % Check\n            assert( isa( value, 'double' ), 'uix:InvalidPropertyValue', ...\n                'Property ''MinimumWidths'' must be of type double.' )\n            assert( all( isreal( value ) ) && ~any( isinf( value ) ) && ...\n                all( value >= 0 ), 'uix:InvalidPropertyValue', ...\n                'Elements of property ''MinimumWidths'' must be non-negative.' )\n            assert( isequal( size( value ), size( obj.Widths_ ) ), ...\n                'uix:InvalidPropertyValue', ...\n                'Size of property ''MinimumWidths'' must match size of contents.' )\n            \n            % Set\n            obj.MinimumWidths_ = value;\n            \n            % Mark as dirty\n            obj.Dirty = true;\n            \n        end % set.MinimumWidths\n        \n    end % accessors\n    \n    methods( Access = protected )\n        \n        function redraw( obj )\n            %redraw  Redraw\n            %\n            %  c.redraw() redraws the container c.\n            \n            % Compute positions\n            bounds = hgconvertunits( ancestor( obj, 'figure' ), ...\n                [0 0 1 1], 'normalized', 'pixels', obj );\n            widths = obj.Widths_;\n            minimumWidths = obj.MinimumWidths_;\n            padding = obj.Padding_;\n            spacing = obj.Spacing_;\n            c = numel( widths );\n            xSizes = uix.calcPixelSizes( bounds(3), widths, ...\n                minimumWidths, padding, spacing );\n            xPositions = [cumsum( [0; xSizes(1:c-1,:)] ) + padding + ...\n                spacing * transpose( 0:c-1 ) + 1, xSizes];\n            yPositions = [padding + 1, max( bounds(4) - 2 * padding, 1 )];\n            yPositions = repmat( yPositions, [c 1] );\n            positions = [xPositions(:,1), yPositions(:,1), ...\n                xPositions(:,2), yPositions(:,2)];\n            \n            % Set positions\n            children = obj.Contents_;\n            for ii = 1:numel( children )\n                uix.setPosition( children(ii), positions(ii,:), 'pixels' )\n            end\n            \n        end % redraw\n        \n        function addChild( obj, child )\n            %addChild  Add child\n            %\n            %  c.addChild(d) adds the child d to the container c.\n            \n            % Add to sizes\n            obj.Widths_(end+1,:) = -1;\n            obj.MinimumWidths_(end+1,:) = 1;\n            \n            % Call superclass method\n            addChild@uix.Box( obj, child )\n            \n        end % addChild\n        \n        function removeChild( obj, child )\n            %removeChild  Remove child\n            %\n            %  c.removeChild(d) removes the child d from the container c.\n            \n            % Remove from sizes\n            tf = obj.Contents_ == child;\n            obj.Widths_(tf,:) = [];\n            obj.MinimumWidths_(tf,:) = [];\n            \n            % Call superclass method\n            removeChild@uix.Box( obj, child )\n            \n        end % removeChild\n        \n        function reorder( obj, indices )\n            %reorder  Reorder contents\n            %\n            %  c.reorder(i) reorders the container contents using indices\n            %  i, c.Contents = c.Contents(i).\n            \n            % Reorder\n            obj.Widths_ = obj.Widths_(indices,:);\n            obj.MinimumWidths_ = obj.MinimumWidths_(indices,:);\n            \n            % Call superclass method\n            reorder@uix.Box( obj, indices )\n            \n        end % reorder\n        \n    end % template methods\n    \nend % classdef"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/Node.m",
    "content": "classdef ( Hidden ) Node < dynamicprops\n    %uix.Node  Node\n    %\n    %  n = uix.Node(o) creates a node for the handle o.\n    %\n    %  Node is a helper class for managing trees of objects and associated\n    %  listeners.\n    \n    %  Copyright 2009-2015 The MathWorks, Inc.\n    %  $Revision: 1165 $ $Date: 2015-12-06 03:09:17 -0500 (Sun, 06 Dec 2015) $\n    \n    properties( SetAccess = private )\n        Object % object\n        Children = uix.Node.empty( [0 1] ) % children\n    end\n    \n    properties( Access = private )\n        ChildListeners = event.listener.empty( [0 1] ) % internal listeners\n    end\n    \n    methods\n        \n        function obj = Node( object )\n            %uix.Node  Node\n            %\n            %  n = uix.Node(o) creates a node for the handle o.\n            \n            % Check\n            assert( isa( object, 'handle' ) && ...\n                isequal( size( object ), [1 1] ) && isvalid( object ), ...\n                'uix:InvalidArgument', 'Object must be a handle.' )\n            \n            % Set properties\n            obj.Object = object;\n            \n        end % constructor\n        \n    end % structors\n    \n    methods\n        \n        function addChild( obj, child )\n            %addChild  Add child\n            %\n            %  n.addChild(c) adds the child node c to the parent node n.\n            \n            % Check\n            assert( isa( child, 'uix.Node' ) && ...\n                isequal( size( child ), [1 1] ), ...\n                'uix:InvalidArgument', 'Invalid node.' )\n            \n            % Add\n            childListener = event.listener( child, ...\n                'ObjectBeingDestroyed', @obj.onChildDeleted );\n            obj.Children(end+1,:) = child;\n            obj.ChildListeners(end+1,:) = childListener;\n            \n        end % addChild\n        \n        function removeChild( obj, child )\n            %removeChild  Remove child\n            %\n            %  n.removeChild(c) removes the child node c from the parent\n            %  node n.\n            \n            % Check\n            assert( isa( child, 'uix.Node' ) && ...\n                isequal( size( child ), [1 1] ), ...\n                'uix:InvalidArgument', 'Invalid node.' )\n            assert( ismember( child, obj.Children ), ...\n                'uix:ItemNotFound', 'Node not found.' )\n            \n            % Remove\n            tf = child == obj.Children;\n            obj.Children(tf,:) = [];\n            obj.ChildListeners(tf,:) = [];\n            \n        end % removeChild\n        \n    end % public methods\n    \n    methods( Access = private )\n        \n        function onChildDeleted( obj, source, ~ )\n            %onChildDeleted  Event handler for deletion of child nodes\n            \n            % Remove\n            obj.removeChild( source )\n            \n        end % onChildDeleted\n        \n    end % event handlers\n    \nend % classdef"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/Panel.m",
    "content": "classdef Panel < matlab.ui.container.Panel & uix.mixin.Panel\n    %uix.Panel  Standard panel\n    %\n    %  b = uix.Panel(p1,v1,p2,v2,...) constructs a standard panel and sets\n    %  parameter p1 to value v1, etc.\n    %\n    %  A card panel is a standard panel (uipanel) that shows one its\n    %  contents and hides the others.\n    %\n    %  See also: uix.CardPanel, uix.BoxPanel, uipanel\n    \n    %  Copyright 2009-2016 The MathWorks, Inc.\n    %  $Revision: 1436 $ $Date: 2016-11-17 17:53:29 +0000 (Thu, 17 Nov 2016) $\n    \n    methods\n        \n        function obj = Panel( varargin )\n            %uix.Panel  Standard panel constructor\n            %\n            %  p = uix.Panel() constructs a standard panel.\n            %\n            %  p = uix.Panel(p1,v1,p2,v2,...) sets parameter p1 to value\n            %  v1, etc.\n            \n            % Set properties\n            if nargin > 0\n                try\n                    assert( rem( nargin, 2 ) == 0, 'uix:InvalidArgument', ...\n                        'Parameters and values must be provided in pairs.' )\n                    set( obj, varargin{:} )\n                catch e\n                    delete( obj )\n                    e.throwAsCaller()\n                end\n            end\n            \n        end % constructor\n        \n    end % structors\n    \n    methods( Access = protected )\n        \n        function redraw( obj )\n            \n            % Compute positions\n            bounds = hgconvertunits( ancestor( obj, 'figure' ), ...\n                [0 0 1 1], 'normalized', 'pixels', obj );\n            padding = obj.Padding_;\n            xSizes = uix.calcPixelSizes( bounds(3), -1, 1, padding, 0 );\n            ySizes = uix.calcPixelSizes( bounds(4), -1, 1, padding, 0 );\n            position = [padding+1 padding+1 xSizes ySizes];\n            \n            % Redraw contents\n            selection = obj.Selection_;\n            if selection ~= 0\n                uix.setPosition( obj.Contents_(selection), position, 'pixels' )\n            end\n            \n        end % redraw\n        \n    end % template methods\n    \nend % classdef"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/PointerManager.m",
    "content": "classdef ( Hidden, Sealed ) PointerManager < handle\n    %uix.PointerManager  Pointer manager\n    \n    %  Copyright 2016 The MathWorks, Inc.\n    %  $Revision: 1435 $ $Date: 2016-11-17 17:50:34 +0000 (Thu, 17 Nov 2016) $\n    \n    properties( SetAccess = private )\n        Figure % figure\n    end\n    \n    properties( Access = private )\n        Tokens % tokens\n        Pointers % pointers\n        NextToken % next token\n        PointerListener % listener\n    end\n    \n    methods( Access = private )\n        \n        function obj = PointerManager( figure )\n            %uix.PointerManager  Create pointer manager\n            %\n            %  m = uix.PointerManager(f) creates a pointer manager for the\n            %  figure f.\n            \n            obj.Figure = figure;\n            obj.Tokens = 0;\n            obj.Pointers = {figure.Pointer};\n            obj.NextToken = 1;\n            obj.PointerListener = event.proplistener( figure, ...\n                findprop( figure, 'Pointer' ), 'PostSet', ...\n                @obj.onPointerChanged );\n            \n        end % constructor\n        \n    end % structors\n    \n    methods( Access = private )\n        \n        function doSetPointer( obj, token, pointer )\n            %doSetPointer  Set pointer\n            %\n            %  m.doSetPointer(t,p) sets the pointer to p with the token t.\n            \n            % Remove old entry\n            tf = obj.Tokens == token;\n            obj.Tokens(tf) = [];\n            obj.Pointers(tf) = [];\n            \n            % Add new entry\n            obj.Tokens(end+1) = token;\n            obj.Pointers{end+1} = pointer;\n            \n            % Set pointer\n            obj.PointerListener.Enabled = false;\n            obj.Figure.Pointer = pointer;\n            obj.PointerListener.Enabled = true;\n            \n        end % doSetPointer\n        \n        function doUnsetPointer( obj, token )\n            %doUnsetPointer  Unset pointer\n            %\n            %  m.doUnsetPointer(s) unsets the pointer with the token t.\n            \n            % Remove old entry\n            tf = obj.Tokens == token;\n            obj.Tokens(tf) = [];\n            obj.Pointers(tf) = [];\n            \n            % Update pointer\n            obj.PointerListener.Enabled = false;\n            obj.Figure.Pointer = obj.Pointers{end};\n            obj.PointerListener.Enabled = true;\n            \n        end % doUnsetPointer\n        \n    end % private methods\n    \n    methods\n        \n        function onPointerChanged( obj, ~, ~ )\n            %onPointerChanged  Event handler\n            \n            % Log as unknown setter\n            obj.doSetPointer( 0, obj.Figure.Pointer )\n            \n        end % onPointerChanged\n        \n    end % event handlers\n    \n    methods( Static )\n        \n        function token = setPointer( figure, pointer )\n            %setPointer  Set pointer\n            %\n            %  t = uix.PointerManager.setPointer(f,p) sets the pointer of\n            %  the figure f to p.  The returned token t can be used\n            %  subsequently to unset the pointer.\n            \n            % Get pointer manager\n            obj = uix.PointerManager.getInstance( figure );\n            \n            % Retrieve token\n            token = obj.NextToken;\n            \n            % Set\n            obj.doSetPointer( token, pointer )\n            \n            % Increment token\n            obj.NextToken = token + 1;\n            \n        end % setPointer\n        \n        function unsetPointer( figure, token )\n            %unsetPointer  Unset pointer\n            %\n            %  uix.PointerManager.unsetPointer(f,t) unsets the pointer of\n            %  the figure f using the token t.\n            \n            % Check ID\n            validateattributes( token, {'numeric'}, {'scalar','integer','>',0} )\n            \n            % Get pointer manager\n            obj = uix.PointerManager.getInstance( figure );\n            \n            % Unset\n            obj.doUnsetPointer( token )\n            \n        end % unsetPointer\n        \n        function obj = getInstance( figure )\n            %getInstance  Get pointer manager\n            %\n            %  m = uix.PointerManager.getInstance(f) gets the pointer\n            %  manager for the figure f.\n            \n            % Get pointer manager\n            name = 'UIxPointerManager';\n            if isprop( figure, name ) % existing, retrieve\n                obj = figure.( name );\n            else % new, create and store\n                obj = uix.PointerManager( figure );\n                p = addprop( figure, name );\n                p.Hidden = true;\n                figure.( name ) = obj;\n            end\n            \n        end % getInstance\n        \n    end % static methods\n    \nend % classdef"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/SelectionData.m",
    "content": "classdef( Hidden, Sealed ) SelectionData < event.EventData\n    %uix.SelectionData  Event data for selection event\n    %\n    %  e = uix.SelectionData(o,n) creates event data including the old\n    %  value o and the new value n.\n    \n    %  Copyright 2009-2015 The MathWorks, Inc.\n    %  $Revision: 1165 $ $Date: 2015-12-06 03:09:17 -0500 (Sun, 06 Dec 2015) $\n    \n    properties( SetAccess = private )\n        OldValue % old value\n        NewValue % newValue\n    end\n    \n    methods\n        \n        function obj = SelectionData( oldValue, newValue )\n            %uix.SelectionData  Event data for selection event\n            %\n            %  e = uix.SelectionData(o,n) creates event data including the\n            %  old value o and the new value n.\n            \n            % Set properties\n            obj.OldValue = oldValue;\n            obj.NewValue = newValue;\n            \n        end % constructor\n        \n    end % structors\n    \nend % classdef"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/VBox.m",
    "content": "classdef VBox < uix.Box\n    %uix.VBox  Vertical box\n    %\n    %  b = uix.VBox(p1,v1,p2,v2,...) constructs a vertical box and sets\n    %  parameter p1 to value v1, etc.\n    %\n    %  A vertical box lays out contents from top to bottom.\n    %\n    %  See also: uix.HBox, uix.Grid, uix.VButtonBox, uix.VBoxFlex\n    \n    %  Copyright 2009-2016 The MathWorks, Inc.\n    %  $Revision: 1436 $ $Date: 2016-11-17 17:53:29 +0000 (Thu, 17 Nov 2016) $\n    \n    properties( Access = public, Dependent, AbortSet )\n        Heights % heights of contents, in pixels and/or weights\n        MinimumHeights % minimum heights of contents, in pixels\n    end\n    \n    properties( Access = protected )\n        Heights_ = zeros( [0 1] ) % backing for Heights\n        MinimumHeights_ = zeros( [0 1] ) % backing for MinimumHeights\n    end\n    \n    methods\n        \n        function obj = VBox( varargin )\n            %uix.VBox  Vertical box constructor\n            %\n            %  b = uix.VBox() constructs a horizontal box.\n            %\n            %  b = uix.VBox(p1,v1,p2,v2,...) sets parameter p1 to value v1,\n            %  etc.\n            \n            % Set properties\n            if nargin > 0\n                try\n                    assert( rem( nargin, 2 ) == 0, 'uix:InvalidArgument', ...\n                        'Parameters and values must be provided in pairs.' )\n                    set( obj, varargin{:} )\n                catch e\n                    delete( obj )\n                    e.throwAsCaller()\n                end\n            end\n            \n        end % constructor\n        \n    end % structors\n    \n    methods\n        \n        function value = get.Heights( obj )\n            \n            value = obj.Heights_;\n            \n        end % get.Heights\n        \n        function set.Heights( obj, value )\n            \n            % For those who can't tell a column from a row...\n            if isrow( value )\n                value = transpose( value );\n            end\n            \n            % Check\n            assert( isa( value, 'double' ), 'uix:InvalidPropertyValue', ...\n                'Property ''Heights'' must be of type double.' )\n            assert( all( isreal( value ) ) && ~any( isinf( value ) ) && ...\n                ~any( isnan( value ) ), 'uix:InvalidPropertyValue', ...\n                'Elements of property ''Heights'' must be real and finite.' )\n            assert( isequal( size( value ), size( obj.Contents_ ) ), ...\n                'uix:InvalidPropertyValue', ...\n                'Size of property ''Heights'' must match size of contents.' )\n            \n            % Set\n            obj.Heights_ = value;\n            \n            % Mark as dirty\n            obj.Dirty = true;\n            \n        end % set.Heights\n        \n        function value = get.MinimumHeights( obj )\n            \n            value = obj.MinimumHeights_;\n            \n        end % get.MinimumHeights\n        \n        function set.MinimumHeights( obj, value )\n            \n            % For those who can't tell a column from a row...\n            if isrow( value )\n                value = transpose( value );\n            end\n            \n            % Check\n            assert( isa( value, 'double' ), 'uix:InvalidPropertyValue', ...\n                'Property ''MinimumHeights'' must be of type double.' )\n            assert( all( isreal( value ) ) && ~any( isinf( value ) ) && ...\n                all( value >= 0 ), 'uix:InvalidPropertyValue', ...\n                'Elements of property ''MinimumHeights'' must be non-negative.' )\n            assert( isequal( size( value ), size( obj.Heights_ ) ), ...\n                'uix:InvalidPropertyValue', ...\n                'Size of property ''MinimumHeights'' must match size of contents.' )\n            \n            % Set\n            obj.MinimumHeights_ = value;\n            \n            % Mark as dirty\n            obj.Dirty = true;\n            \n        end % set.MinimumHeights\n        \n    end % accessors\n    \n    methods( Access = protected )\n        \n        function redraw( obj )\n            %redraw  Redraw\n            %\n            %  c.redraw() redraws the container c.\n            \n            % Compute positions\n            bounds = hgconvertunits( ancestor( obj, 'figure' ), ...\n                [0 0 1 1], 'normalized', 'pixels', obj );\n            heights = obj.Heights_;\n            minimumHeights = obj.MinimumHeights_;\n            padding = obj.Padding_;\n            spacing = obj.Spacing_;\n            r = numel( heights );\n            xPositions = [padding + 1, max( bounds(3) - 2 * padding, 1 )];\n            xPositions = repmat( xPositions, [r 1] );\n            ySizes = uix.calcPixelSizes( bounds(4), heights, ...\n                minimumHeights, padding, spacing );\n            yPositions = [bounds(4) - cumsum( ySizes ) - padding - ...\n                spacing * transpose( 0:r-1 ) + 1, ySizes];\n            positions = [xPositions(:,1), yPositions(:,1), ...\n                xPositions(:,2), yPositions(:,2)];\n            \n            % Set positions\n            children = obj.Contents_;\n            for ii = 1:numel( children )\n                uix.setPosition( children(ii), positions(ii,:), 'pixels' )\n            end\n            \n        end % redraw\n        \n        function addChild( obj, child )\n            %addChild  Add child\n            %\n            %  c.addChild(d) adds the child d to the container c.\n            \n            % Add to sizes\n            obj.Heights_(end+1,:) = -1;\n            obj.MinimumHeights_(end+1,:) = 1;\n            \n            % Call superclass method\n            addChild@uix.Box( obj, child )\n            \n        end % addChild\n        \n        function removeChild( obj, child )\n            %removeChild  Remove child\n            %\n            %  c.removeChild(d) removes the child d from the container c.\n            \n            % Remove from sizes\n            tf = obj.Contents_ == child;\n            obj.Heights_(tf,:) = [];\n            obj.MinimumHeights_(tf,:) = [];\n            \n            % Call superclass method\n            removeChild@uix.Box( obj, child )\n            \n        end % removeChild\n        \n        function reorder( obj, indices )\n            %reorder  Reorder contents\n            %\n            %  c.reorder(i) reorders the container contents using indices\n            %  i, c.Contents = c.Contents(i).\n            \n            % Reorder\n            obj.Heights_ = obj.Heights_(indices,:);\n            obj.MinimumHeights_ = obj.MinimumHeights_(indices,:);\n            \n            % Call superclass method\n            reorder@uix.Box( obj, indices )\n            \n        end % reorder\n        \n    end % template methods\n    \nend % classdef"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/calcPixelSizes.m",
    "content": "function pSizes = calcPixelSizes( pTotal, mSizes, pMinima, pPadding, pSpacing )\n%calcPixelSizes  Calculate child sizes in pixels\n%\n%  pSizes = uix.calcPixelSizes(total,mSizes,minSizes,padding,spacing)\n%  computes child sizes (in pixels) given total available size (in pixels),\n%  child sizes (in pixels and/or relative), minimum child sizes (in\n%  pixels), padding (in pixels) and spacing (in pixels).\n%\n%  Notes:\n%  * All children are at least as large as the minimum specified size\n%  * Relative sizes are respected for children larger than then minimum\n%  specified size\n%  * Children may extend beyond the total available size if the minimum\n%  sizes, padding and spacing are too large\n\n%  Copyright 2009-2015 The MathWorks, Inc.\n%  $Revision: 1182 $ $Date: 2015-12-07 14:27:30 -0500 (Mon, 07 Dec 2015) $\n\n% Initialize\npSizes = NaN( size( mSizes ) ); % output\nn = numel( mSizes ); % need this later\n\n% Apply absolute sizes\na = mSizes >= 0; % absolute\npSizes(a) = max( mSizes(a), pMinima(a) );\n\nwhile true\n    \n    u = isnan( pSizes ); % unsolved\n    pUnsolvedTotal = pTotal - max( (n-1), 0 ) * pSpacing ...\n        - 2 * sign( n ) * pPadding - sum( pSizes(~u) );\n    pUnsolvedSizes = mSizes(u) / sum( mSizes(u) ) * pUnsolvedTotal;\n    pUnsolvedMinima = pMinima(u);\n    s = pUnsolvedSizes < pUnsolvedMinima; % small\n    if any( s )\n        pUnsolvedSizes(s) = pUnsolvedMinima(s);\n        pUnsolvedSizes(~s) = NaN;\n        pSizes(u) = pUnsolvedSizes;\n        % repeat\n    else\n        pSizes(u) = pUnsolvedSizes;\n        break % done\n    end\n    \nend\n\nend % calcPixelSizes"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/setPosition.m",
    "content": "function setPosition( o, p, u )\n%setPosition  Set position of graphics object\n%\n%  setPosition(o,p,u) sets the position of a graphics object o to value p\n%  with units u.\n%\n%  In contrast to setting the Position property directly, this function\n%  honors the ActivePositionProperty of axes.\n\n%  Copyright 2009-2016 The MathWorks, Inc.\n%  $Revision: 1435 $ $Date: 2016-11-17 17:50:34 +0000 (Thu, 17 Nov 2016) $\n\no.Units = u;\nif isa( o, 'matlab.graphics.axis.Axes' )\n    switch o.ActivePositionProperty\n        case 'position'\n            o.Position = p;\n        case 'outerposition'\n            o.OuterPosition = p;\n        otherwise\n            error( 'uix:InvalidState', ...\n                'Unknown value ''%s'' for property ''ActivePositionProperty'' of %s.', ...\n                o.ActivePositionProperty, class( o ) )\n    end\nelse\n    o.Position = p;\nend\n\nend % setPosition"
  },
  {
    "path": "leap/toolbox/graphics/GUI Layout Toolbox/layout/+uix/tracking.m",
    "content": "function varargout = tracking( varargin )\n%tracking  Track anonymized usage data\n%\n%  tracking(p,v,id) tracks usage to the property p for the product version\n%  v and identifier id.  No personally identifiable information is tracked.\n%\n%  r = tracking(...) returns the server response r, for debugging purposes.\n%\n%  tracking('on') turns tracking on.  tracking('off') turns tracking off.\n%  tracking('query') returns the tracking state.\n\n%  tracking('spoof') sets the tracking settings -- domain, language,\n%  client, MATLAB version, operating system version -- to spoof values.\n%  tracking('reset') sets the tracking settings to normal values.\n%\n%  [t,s] = tracking('query') returns the tracking state t and settings s.\n\n%  Copyright 2016 The MathWorks, Inc.\n%  $Revision: 1435 $ $Date: 2016-11-17 17:50:34 +0000 (Thu, 17 Nov 2016) $\n\npersistent STATE USERNAME DOMAIN LANGUAGE CLIENT MATLAB OS\nif isempty( STATE )\n    STATE = getpref( 'Tracking', 'State', 'on' );\n    if strcmp( STATE, 'snooze' ) % deprecated\n        setpref( 'Tracking', 'State', 'on' )\n        STATE = 'on';\n    end\n    if ispref( 'Tracking', 'Date' ) % deprecated\n        rmpref( 'Tracking', 'Date' )\n    end\n    USERNAME = getenv( 'USERNAME' );\n    reset()\nend % initialize\n\nswitch nargin\n    case 1\n        switch varargin{1}\n            case {'on','off'}\n                STATE = varargin{1};\n                setpref( 'Tracking', 'State', varargin{1} ) % persist\n            case 'spoof'\n                spoof()\n            case 'reset'\n                reset()\n            case 'query'\n                varargout{1} = STATE;\n                varargout{2} = query();\n            otherwise\n                error( 'tracking:InvalidArgument', ...\n                    'Valid options are ''on'', ''off'' and ''query''.' )\n        end\n    case 3\n        switch nargout\n            case 0\n                if strcmp( STATE, 'off' ), return, end\n                uri = 'https://www.google-analytics.com/collect';\n                track( uri, varargin{:} );\n            case 1\n                uri = 'https://www.google-analytics.com/debug/collect';\n                varargout{1} = track( uri, varargin{:} );\n            otherwise\n                nargoutchk( 0, 1 )\n        end\n    otherwise\n        narginchk( 3, 3 )\nend % switch\n\n    function reset()\n        %reset  Set normal settings\n        \n        DOMAIN = lower( getenv( 'USERDOMAIN' ) );\n        LANGUAGE = char( java.util.Locale.getDefault() );\n        CLIENT = getpref( 'Tracking', 'Client', uuid() );\n        MATLAB = matlab();\n        OS = os();\n        \n    end % reset\n\n    function spoof()\n        %spoof  Set spoof settings\n        \n        DOMAIN = randomDomain();\n        LANGUAGE = randomLanguage();\n        CLIENT = randomClient();\n        MATLAB = randomMatlab();\n        OS = randomOs();\n        \n    end % spoof\n\n    function s = query()\n        %query  Return settings\n        \n        s.Username = USERNAME;\n        s.Domain = DOMAIN;\n        s.Language = LANGUAGE;\n        s.Client = CLIENT;\n        s.Matlab = MATLAB;\n        s.Os = OS;\n        \n    end % query\n\n    function varargout = track( uri, p, v, s )\n        %track  Do tracking\n        \n        a = sprintf( '%s/%s (%s)', MATLAB, v, OS );\n        if isdeployed()\n            ds = 'deployed';\n        elseif strcmp( DOMAIN, 'mathworks' )\n            ds = DOMAIN;\n        else\n            ds = 'unknown';\n        end\n        pv = {'v', '1', 'tid', p, 'ua', escape( a ), 'ul', LANGUAGE, ...\n            'cid', CLIENT, 'ht', 'pageview', ...\n            'dp', sprintf( '/%s', s ), 'ds', ds};\n        [varargout{1:nargout}] = urlread( uri, 'Post', pv );\n        \n    end % track\n\nend % tracking\n\nfunction s = randomDomain()\n%randomDomain  Random domain string\n\nswitch randi( 4 )\n    case 1\n        s = 'mathworks';\n    otherwise\n        s = hash( uuid() );\nend\n\nend % randomDomain\n\nfunction s = randomLanguage()\n%randomLanguage  Random language string\n\nlo = java.util.Locale.getAvailableLocales();\ns = char( lo(randi( numel( lo ) )) );\n\nend % randomLanguage\n\nfunction s = randomClient()\n%randomClient  Random client identifier\n\ns = uuid();\n\nend % randomClient\n\nfunction s = matlab()\n%matlab  MATLAB version string\n\nv = ver( 'MATLAB' );\ns = v.Release;\ns(s=='('|s==')') = [];\n\nend % matlab\n\nfunction s = randomMatlab()\n%randomMatlab  Random MATLAB version string\n\nreleases = {'R2014b' 'R2015a' 'R2015b' 'R2016a' 'R2016b'};\ns = releases{randi( numel( releases ) )};\n\nend % randomMatlab\n\nfunction s = os()\n%os  Operating system string\n\nif ispc()\n    s = sprintf( 'Windows NT %s', ...\n        char( java.lang.System.getProperty( 'os.version' ) ) );\nelseif isunix()\n    s = 'Linux x86_64';\nelseif ismac()\n    s = sprintf( 'Macintosh; Intel OS X %s', ...\n        strrep( char( java.lang.System.getProperty( 'os.version' ) ), ' ', '_' ) );\nelse\n    s = 'unknown';\nend\n\nend % os\n\nfunction s = randomOs()\n%randomOs  Random operating system string\n\nswitch randi( 3 )\n    case 1\n        versions = [5.1 5.2 6 6.1 6.2 6.3 10];\n        s = sprintf( 'Windows NT %.1f', ...\n            versions(randi( numel( versions ) )) );\n    case 2\n        s = 'Linux x86_64';\n    case 3\n        s = sprintf( 'Macintosh; Intel OS X 10_%d', ...\n            randi( [10 12] ) );\nend\n\nend % randomOs\n\nfunction s = escape( s )\n%escape  Escape string\n\ns = char( java.net.URLEncoder.encode( s, 'UTF-8' ) );\n\nend % escape\n\nfunction h = hash( s )\n%hash  Hash string\n%\n%  See also: rptgen.hash\n\npersistent MD5\nif isempty( MD5 )\n    MD5 = java.security.MessageDigest.getInstance( 'MD5' );\nend\n\nMD5.update( uint8( s(:) ) );\nh = typecast( MD5.digest, 'uint8' );\nh = dec2hex( h )';\nh = lower( h(:) )';\n\nend % hash\n\nfunction s = uuid()\n%uuid  Unique identifier\n\ns = char( java.util.UUID.randomUUID() );\n\nend % uuid"
  },
  {
    "path": "leap/toolbox/graphics/distributionPlot/colorCode2rgb.m",
    "content": "function rgbVec = colorCode2rgb(c)\n%COLORCODE2RGB converts a color code to an rgb vector\n%\n\n% SYNOPSIS rgbVec = colorCode2rgb(c)\n%\n% INPUT c : color code\n%       The following colors are supported:\n%        'y' 'yellow'\n%        'm' 'magenta'\n%        'c' 'cyan'\n%        'r' 'red'\n%        'g' 'green'\n%        'b' 'blue'\n%        'w' 'white'\n%        'k' 'black'\n% \n% OUTPUT rgbVec : vector with the rgb value\n%\n% EXAMPLE \n%    rgb = colorCode2rgb('r')\n%    rgb =\n%          [1 0 0]\n\nif iscell(c) \n    rgbVec = cell2mat(cellfun(@colorCode2rgb,c,'uni',false));\n    return\nend\n\nswitch c\ncase {'y','yellow'}, rgbVec = [1,1,0];\ncase {'m','magenta'}, rgbVec = [1,0,1];\ncase {'c','cyan'}, rgbVec = [0,1,1];\ncase {'r','red'}, rgbVec = [1,0,0];\ncase {'g','green'}, rgbVec = [0,1,0];\ncase {'b','blue'}, rgbVec = [0,0,1];\ncase {'w','white'}, rgbVec = [1,1,1];\ncase {'k','black'}, rgbVec = [0,0,0];\notherwise, error('unknown color code %s',c)\nend;\n"
  },
  {
    "path": "leap/toolbox/graphics/draggable/draggable.m",
    "content": "function draggable(h,varargin)\n% DRAGGABLE - Make it so that a graphics object can be dragged in a figure.\n%   This function makes an object interactive by allowing it to be dragged\n%   accross a set of axes, following or not certain constraints. This\n%   allows for intuitive control elements which are not buttons or other\n%   standard GUI objects, and which reside inside an axis. Typical use\n%   involve markers on an axis, whose position alters the output of a\n%   computation or display\n% \n%   >> draggable(h);\n%   \n%   makes the object with handle \"h\" draggable. Use the \"Position\" property\n%   of the object to retrieve its position, by issuing a get(h,'Position')\n%   command.\n% \n%   If h is a vector of handles, then draggable is called on each handle\n%   using the same following arguments, if any.\n%\n%   >> draggable(h,...,motionfcn)\n%\n%   where \"motionfcn\" is a function handle, executes the given function\n%   while the object is dragged. Handle h is passed to motionfcn as an\n%   argument. Argument \"motionfcn\" can be put anywhere after handle \"h\".\n%\n%   >> draggable(h,...,constraint,p);\n%\n%   enables the object with handle \"h\" to be dragged, with a constraint.\n%   Arguments \"constraint\" (a string) and \"p\" (a vector) can be put\n%   anywhere after handle \"h\".\n%\n%   >> draggable(h,...,'endfcn',endfcn);\n%\n%   where \"endfcn\" is a function handle, executes the given function AFTER\n%   the object is dragged (more specifically, on the next WindowButtonUp \n%   event). The function handle must come after the string 'endfcn', to \n%   avoid ambiguity with the \"motionfcn\" argument (above). Handle h is \n%   passed to endfcn as an argument.\n%\n%   >> draggable(h,'off')\n%\n%   returns object h to its original, non-draggable state.\n%\n%   CONSTRAINTS\n%\n%   The argument \"constraint\" may be one of the following strings:\n%\n%       'n' or 'none':          The object is unconstrained (default).\n%       'h' or 'horizontal':    The object can only be moved horizontally.\n%       'v' or 'vertical':      The object can only be moved vertically.\n%       'd' or 'diagonal':      The object can only be moved along an\n%                               arbitrary line of a given slope.\n%\n%   The argument \"p\" is an optional parameter which depends upon the\n%   constraint type:\n%\n%   Constraint      p                   Description\n%   -----------------------------------------------------------------------\n%\n%   'none'          [x1 x2 y1 y2]       Drag range (for the object's outer\n%                                       limits, from x1 to x2 on the x-axis\n%                                       and from y1 to y2 on the y-axis).\n%                                       Default is the current axes range.\n%                                       Use \"inf\" if no limit is desired.\n%\n%   'horizontal'    [xmin xmax]         Drag range (for the object's outer\n%                                       limits). Default is the x-axis\n%                                       range. Use \"inf\" if no limit is\n%                                       desired. Note that full limits of\n%                                       the form [x1 x2 y1 y2] can also be\n%                                       used.\n%\n%   'vertical'      [ymin ymax]         Drag range (for the object's outer\n%                                       limits). Default is the y-axis\n%                                       range. Use \"inf\" if no limit is\n%                                       desired. Note that full limits of\n%                                       the form [x1 x2 y1 y2] can also be\n%                                       used.\n%\n%   'diagonal'      [m x1 x2 y1 y2]     Slope m of the line along which the\n%                                       movement is constrained (default is\n%                                       1); x1 x2 y1 y2 as in 'none'.\n%\n%   -----------------------------------------------------------------------\n%\n\n\n\n% VERSION INFORMATION:\n% 2003-11-20:   Initially submitted to MatlabCentral.Com\n% 2004-01-06:   Addition of the renderer option, as proposed by Ohad Gal\n%               as a feedback on MatlabCentral.Com.\n% 2004-02-18:   Bugfix: now works with 1-element plots and line objects\n% 2004-03-04:   Bugfix: sanitized the way the object's new position is\n%               computed; it now always follow the mouse even after the\n%               mouse pointer was out of the axes.\n% 2004-03-05:   Bugfix: movement when mouse is out of the axes is now\n%               definitely correct ;)\n% 2006-05-23:   Bugfix: fix a rendering issue using Matlab 7 & +\n%               Deprecated the rendering options: rendering seems ok with\n%               every renderer.\n% 2010-01-11:   Bugfix by Gilles Fortin (odd jumping on limits caused by\n%               round-off error due to successive addition then subtraction\n%               of the same value)\n% 2010-02-26:   endfcn code by Steven Bierer included.\n%               Some suggested M-Lint fixes performed.\n% 2012-01-18:   - Refactoring\n%               - Limits of the form [x1 x2 y1 y2] can be used for 'h' and\n%                 'v' constraint types;\n%               - Support for text objects;\n%               - Added diagonal constraint type\n% 2012-01-20:   - Tested\n%               - Added support for h as a vector of handles\n%               - 'sliders' demo added in dragdemo\n% 2013-01-10:   Bugfix: finding the figure's handle through gcbf in order \n%               to fix a bug when axes are embedded into a Panel. \n%               (Bug found by Esmerald Aliai)\n\n\n% IMPLEMENTATION NOTES:\n%\n% This function uses the dragged object's \"ButtonDownFcn\" function and set\n% it so that the objec becomes draggable. Any previous \"ButtonDownFcn\" is \n% thus lost during operation, but is retrieved after issuing the \n% draggable(h,'off') command.\n%\n% Information about the object's behavior is also stored in the object's\n% 'UserData' property, using setappdata() and getappdata(). The original\n% 'UserData' property is restored after issuing the draggable(h,'off')\n% command.\n%\n% The corresponding figure's \"WindowButtonDownFcn\", \"WindowButtonUpFcn\" and\n% \"WindowButtonMotionFcn\" functions.  During operation, those functions are\n% set by DRAGGABLE; however, the original ones are restored after the user\n% stops dragging the object.\n%\n% By default, DRAGGABLE also switches the figure's renderer to 'zbuffer'\n% during operation: 'painters' is not fast enough and 'opengl' sometimes\n% produce curious results. However there may be a need to switch to another\n% renderer, so the user can now specify a specific figure renderer during\n% object drag (thanks to Ohad Gal for the suggestion).\n%\n% The \"motionfcn\" function handle is called at each displacement, after the\n% object's position is updated, using \"feval(motionfcn,h)\", where h is the\n% object's handle.\n\n% =========================================================================\n% Copyright (C) 2003-2012\n% Francois Bouffard\n% fbouffard@gmail.com\n% =========================================================================\n\n% =========================================================================\n% Input arguments management\n% =========================================================================\n\n% If h is a vector of handle, applying draggable on each object and\n% returning.\nif length(h) > 1\n    for k = 1:length(h)\n        draggable(h(k),varargin{:});\n    end\n    return\nend\n\n% Initialization of some default arguments\nuser_renderer = 'zbuffer';\nuser_movefcn = [];\nconstraint = 'none';\np = [];\nuser_endfcn = [];       % added by SMB (see 'for k' loop below)\nendinput = 0;\n\n% At least the handle to the object must be given\nNarg = nargin;\nif Narg == 0\n    error('Not engough input arguments');\nelseif numel(h)>1\n    error('Only one object at a time can be made draggable');\nend;\n\n% Fetching informations about the parent axes\naxh = get(h,'Parent');\nif iscell(axh)\n    axh = axh{1};\nend;\n%fgh = get(axh,'Parent'); % This fails if the axes are embedded in a Panel\nfgh = gcbf; % This should always work\nax_xlim = get(axh,'XLim');\nax_ylim = get(axh,'YLim');\n\n% Assigning optional arguments\nNoptarg = Narg - 1;\nfor k = 1:Noptarg\n   current_arg = varargin{k};\n   if isa(current_arg,'function_handle') && endinput\n       user_endfcn = current_arg; % added by SMB\n       endinput = 0;              % 'movefcn' can still be a later argument\n   elseif isa(current_arg,'function_handle')\n       user_movefcn = current_arg;\n   end;\n   if ischar(current_arg);\n       switch lower(current_arg)\n           case {'off'}\n               set_initial_state(h);\n               return;\n           case {'painters','zbuffer','opengl'}\n               warning('DRAGGABLE:DEPRECATED_OPTION', ...\n                       'The renderer option is deprecated and will not be taken into account');\n               user_renderer = current_arg;\n           case {'endfcn'} % added by SMB\n               endinput = 1;\n           otherwise\n               constraint = current_arg;\n       end;\n   end;\n   if isnumeric(current_arg);\n       p = current_arg;\n   end;\nend;\n\n% Assigning defaults for constraint parameter\nswitch lower(constraint)\n    case {'n','none'}\n        constraint = 'n';\n        if isempty(p); p = [ax_xlim ax_ylim]; end;\n    case {'h','horizontal'}\n        constraint = 'h';\n        if isempty(p) \n            p = ax_xlim;\n        elseif length(p) == 4\n            p = p(1:2);\n        end\n    case {'v','vertical'}\n        constraint = 'v';\n        if isempty(p)\n            p = ax_ylim; \n        elseif length(p) == 4\n            p = p(3:4);\n        end\n    case {'d','diagonal','l','locked'}\n        constraint = 'd';\n        if isempty(p)\n            p = [1 ax_xlim ax_ylim]; \n        elseif length(p) == 1\n            p = [p ax_xlim ax_ylim];\n        end;\n    otherwise\n        error('Unknown constraint type');\nend;\n\n% =========================================================================\n% Saving initial state and parameters, setting up the object callback\n% =========================================================================\n\n% Saving object's and parent figure's initial state\nsetappdata(h,'initial_userdata',get(h,'UserData'));\nsetappdata(h,'initial_objbdfcn',get(h,'ButtonDownFcn'));\nsetappdata(h,'initial_renderer',get(fgh,'Renderer'));\nsetappdata(h,'initial_wbdfcn',get(fgh,'WindowButtonDownFcn'));\nsetappdata(h,'initial_wbufcn',get(fgh,'WindowButtonUpFcn'));\nsetappdata(h,'initial_wbmfcn',get(fgh,'WindowButtonMotionFcn'));\n\n% Saving parameters\nsetappdata(h,'constraint_type',constraint);\nsetappdata(h,'constraint_parameters',p);\nsetappdata(h,'user_movefcn',user_movefcn);\nsetappdata(h,'user_endfcn',user_endfcn);        % added by SMB\nsetappdata(h,'user_renderer',user_renderer);\n\n% Setting the object's ButtonDownFcn\nset(h,'ButtonDownFcn',@click_object);\n\n% =========================================================================\n% FUNCTION click_object\n%   Executed when the object is clicked\n% =========================================================================\n\nfunction click_object(obj,eventdata)\n% obj here is the object to be dragged and gcf is the object's parent\n% figure since the user clicked on the object\nsetappdata(obj,'initial_position',get_position(obj));\nsetappdata(obj,'initial_extent',compute_extent(obj));\nsetappdata(obj,'initial_point',get(gca,'CurrentPoint'));\nset(gcf,'WindowButtonDownFcn',{@activate_movefcn,obj});\nset(gcf,'WindowButtonUpFcn',{@deactivate_movefcn,obj});\nactivate_movefcn(gcf,eventdata,obj);\n\n% =========================================================================\n% FUNCTION activate_movefcn\n%   Activates the WindowButtonMotionFcn for the figure\n% =========================================================================\n\nfunction activate_movefcn(obj,eventdata,h)\n% We were once setting up renderers here. Now we only set the movefcn\nset(obj,'WindowButtonMotionFcn',{@movefcn,h});\n\n% =========================================================================\n% FUNCTION deactivate_movefcn\n%   Deactivates the WindowButtonMotionFcn for the figure\n% =========================================================================\n\nfunction deactivate_movefcn(obj,eventdata,h)\n% obj here is the figure containing the object\n% Setting the original MotionFcn, DuttonDownFcn and ButtonUpFcn back\nset(obj,'WindowButtonMotionFcn',getappdata(h,'initial_wbmfcn'));\nset(obj,'WindowButtonDownFcn',getappdata(h,'initial_wbdfcn'));\nset(obj,'WindowButtonUpFcn',getappdata(h,'initial_wbufcn'));\n% Executing the user's drag end function\nuser_endfcn = getappdata(h,'user_endfcn');\nif ~isempty(user_endfcn)\n    feval(user_endfcn,h);           % added by SMB, modified by FB\nend\n\n% =========================================================================\n% FUNCTION set_initial_state\n%   Returns the object to its initial state\n% =========================================================================\n\nfunction set_initial_state(h)\ninitial_objbdfcn = getappdata(h,'initial_objbdfcn');\ninitial_userdata = getappdata(h,'initial_userdata');\nset(h,'ButtonDownFcn',initial_objbdfcn);\nset(h,'UserData',initial_userdata);\n\n% =========================================================================\n% FUNCTION movefcn\n%   Actual code for dragging the object\n% =========================================================================\n\nfunction movefcn(obj,eventdata,h)\n% obj here is the *figure* containing the object\n\n% Retrieving data saved in the figure\n% Reminder: \"position\" refers to the object position in the axes\n%           \"point\" refers to the location of the mouse pointer\ninitial_point = getappdata(h,'initial_point');\nconstraint = getappdata(h,'constraint_type');\np = getappdata(h,'constraint_parameters');\nuser_movefcn = getappdata(h,'user_movefcn');\n\n% Getting current mouse position\ncurrent_point = get(gca,'CurrentPoint');\n\n% Computing mouse movement (dpt is [dx dy])\ncpt = current_point(1,1:2);\nipt = initial_point(1,1:2);\ndpt = cpt - ipt;\n\n% Dealing with the pathetic cases of zero or infinite slopes\nif strcmpi(constraint,'d')\n    if p(1) == 0\n        constraint = 'h';\n        p = p(2:end);\n    elseif isinf(p(1))\n        constraint = 'v';\n        p = p(2:end);\n    end\nend\n\n% Computing movement range and imposing movement constraints\n% (p is always [xmin xmax ymin ymax])\nswitch lower(constraint)\n    case 'n'\n        range = p;\n    case 'h'\n        dpt(2) = 0;\n        range = [p -inf inf];\n    case 'v'\n        dpt(1) = 0;\n        range = [-inf inf p];\n    case 'd'\n        % Multiple options here as to how we use dpt to move the object\n        % along a diagonal. \n        % We could use the largest of abs(dpt) for judging movement, but\n        % this causes weird behavior in some cases. E.g. when the slope\n        % is gentle (<1) and dy is the largest, the object will move\n        % rapidly far away from the mouse pointer.\n        % Another option (see below) is to follow dx when the \n        % slope is <1 and dy when the slope is >= 1.\n \n        %if abs(p(1)) >=1\n        %    dpt = [dpt(2)/p(1) dpt(2)];\n        %else\n        %    dpt = [dpt(1) p(1)*dpt(1)];\n        %end\n        \n        % Projecting dpt along the diagonal seems to work really well.\n        v = [1; p(1)];\n        Pv = v*v'/(v'*v);\n        dpt = dpt*Pv;\n        \n        range = p(2:5);\nend\n\n% Computing new position.\n% What we want is actually a bit complex: we want the object to adopt the \n% new position, unless it gets out of range. If it gets out of range in a \n% direction, we want it to stick to the limit in that direction. Also, if \n% the object is out of range at the beginning of the movement, we want to \n% be able to move it back into range; movement must then be allowed.\n\n% For debugging purposes only; setting debug to 1 shows range, extents,\n% dpt, corrected dpt and in-range status of the object in the command\n% window. Note: this will clear the command window.\ndebug = 0;\nidpt = dpt;\n\n% Computing object extent in the [x y w h] format before and after moving\ninitial_extent = getappdata(h,'initial_extent');\nnew_extent = initial_extent + [dpt 0 0];\n\n% Verifying if old and new objects breach the allowed range in any\n% direction (see the function is_inside_range below)\ninitial_inrange = is_inside_range(initial_extent,range);\nnew_inrange = is_inside_range(new_extent,range);\n\n% Modifying dpt to stick to range limit if range violation occured,\n% but the movement won't get restricted if the object was out of\n% range to begin with.\n%\n% We use if/ends and no elseif's because once an object hits a range limit,\n% it is still free to move along the other axis, and another range limit\n% could be hit aftwards. That is, except for diagonal constraints, in \n% which a first limit hit must completely lock the object until the mouse\n% is inside the range.\n\n% In-line correction functions to dpt due to range violations\nxminc = @(dpt) [range(1) - initial_extent(1) dpt(2)];\nxmaxc = @(dpt) [range(2) - (initial_extent(1) + initial_extent(3)) dpt(2)];\nyminc = @(dpt) [dpt(1) range(3) - initial_extent(2)];\nymaxc = @(dpt) [dpt(1) range(4) - (initial_extent(2) + initial_extent(4))];\n\n% We build a list of corrections to apply\ncorrections = {};\nif initial_inrange(1) && ~new_inrange(1)\n    % was within, now out of xmin range -- add xminc\n    corrections = [corrections {xminc}];\nend\nif initial_inrange(2) && ~new_inrange(2)\n    % was within, now out of xmax range -- add xmaxc\n    corrections = [corrections {xmaxc}];\nend\nif initial_inrange(3) && ~new_inrange(3)\n    % was within, now out of ymin range -- add yminc\n    corrections = [corrections {yminc}];\nend\nif initial_inrange(4) && ~new_inrange(4)\n    % was within, now out of ymax range -- add ymaxc\n    corrections = [corrections {ymaxc}];\nend\n\n% Applying all corrections, except for objects following a diagonal\n% constraint, which must stop at the first one\nif ~isempty(corrections)\n    if strcmpi(constraint,'d')\n        c = corrections{1};\n        dpt = c(dpt);\n        % Forcing the object to remain on the diagonal constraint\n        if isequal(c,xminc) || isequal(c,xmaxc) % horizontal correction\n            dpt(2) = p(1)*dpt(1);\n        elseif isequal(c,yminc) || isequal(c,ymaxc) % vertical correction\n            dpt(1) = dpt(2)/p(1);\n        end\n    else\n        % Just applying all corrections\n        for c = corrections\n            dpt = c{1}(dpt);\n        end\n    end\nend\n\n% Debug messages\nif debug\n    if all(new_inrange)\n        status = 'OK';\n    else\n        status = 'RANGE VIOLATION';\n    end\n    clc\n    disp(sprintf('          range: %0.3f %0.3f %0.3f %0.3f', range));\n    disp(sprintf(' initial extent: %0.3f %0.3f %0.3f %0.3f', initial_extent))\n    disp(sprintf('     new extent: %0.3f %0.3f %0.3f %0.3f', new_extent))\n    disp(sprintf('initial inrange: %d %d %d %d', initial_inrange))\n    disp(sprintf('    new inrange: %d %d %d %d [%s]', new_inrange, status))\n    disp(sprintf('    initial dpt: %0.3f %0.3f', idpt))\n    disp(sprintf('  corrected dpt: %0.3f %0.3f', dpt))\nend\n\n% Re-computing new position with modified dpt\nnewpos = update_position(getappdata(h,'initial_position'),dpt);\n\n% Setting the new position which actually moves the object\nset_position(h,newpos);\n\n% Calling user-provided function handle\nif ~isempty(user_movefcn)\n    feval(user_movefcn,h);\nend;\n\n% =========================================================================\n% FUNCTION get_position\n%   Return an object's position: [x y [z / w h]] or [xdata; ydata]\n% =========================================================================\nfunction pos = get_position(obj)\nprops = get(obj);\nif isfield(props,'Position')\n    pos = props.Position;\nelseif isfield(props,'XData')\n    pos = [props.XData(:)'; props.YData(:)'];\nelse\n    error('Unable to find position');\nend\n\n% =========================================================================\n% FUNCTION update_position\n%   Adds dpt to a position specification as returned by get_position\n% =========================================================================\nfunction newpos = update_position(pos,dpt)\nnewpos = pos;\nif size(pos,1) == 1 % [x y [z / w h]]\n    newpos(1:2) = newpos(1:2) + dpt;\nelse                % [xdata; ydata]\n    newpos(1,:) = newpos(1,:) + dpt(1);\n    newpos(2,:) = newpos(2,:) + dpt(2);\nend\n\n% =========================================================================\n% FUNCTION set_position\n%   Sets the position of an object obj using get_position's format\n% =========================================================================\nfunction set_position(obj,pos)\nif size(pos,1) == 1 % 'Position' property\n    set(obj,'Position',pos);\nelse                % 'XData/YData' properties\n    set(obj,'XData',pos(1,:),'YData',pos(2,:));\nend\n\n% =========================================================================\n% FUNCTION compute_extent\n%   Computes an object's extent for different object types;\n%   extent is [x y w h]\n% =========================================================================\n\nfunction extent = compute_extent(obj)\nprops = get(obj);\nif isfield(props,'Extent')\n    extent = props.Extent;\nelseif isfield(props,'Position')\n    extent = props.Position;\nelseif isfield(props,'XData')\n    minx = min(props.XData);\n    miny = min(props.YData);\n    w = max(props.XData) - minx;\n    h = max(props.YData) - miny;\n    extent = [minx miny w h];\nelse\n    error('Unable to compute extent');\nend\n    \n% =========================================================================\n% FUNCTION is_inside_range\n%   Checks if a rectangular object is entirely inside a rectangular range\n% =========================================================================\n\nfunction inrange = is_inside_range(extent,range)\n% extent is in the [x y w h] format\n% range is in the [xmin xmax ymin ymax] format\n% inrange is a 4x1 vector of boolean values corresponding to range limits\ninrange = [extent(1) >= range(1) ...\n           extent(1) + extent(3) <= range(2) ...\n           extent(2) >= range(3) ...\n           extent(2) + extent(4) <= range(4)];"
  },
  {
    "path": "leap/toolbox/graphics/figclosekey.m",
    "content": "function figclosekey(h, key)\n%FIGCLOSEKEY Add a hotkey for closing the figure.\n% Usage:\n%   figclosekey(h, key)\n% \n% Args:\n%   h: figure handle (default: gcf)\n%   key: hotkey to use (default: 'q')\n% \n% See also: event, addlistener\n\nif nargin < 2 || isempty(key); key = 'q'; end\nif nargin == 1 && ischar(h); [h,key] = swap(h,key); end\nif nargin < 1 || isempty(h); h = gcf(); end\n\nif isfield(h.UserData,'hasCloseKey')\n    h = figure();\nend\n\nset(h, 'KeyPressFcn',@(h,evt)KeyPressFcn_cb(h,evt,key));\nh.UserData.hasCloseKey = true;\n\nend\n\nfunction KeyPressFcn_cb(h,evt,key)\n    if strcmp(evt.Key,key)\n        delete(h)\n    end\nend"
  },
  {
    "path": "leap/toolbox/graphics/figsize.m",
    "content": "function sz = figsize(h, width, height)\n%FIGSIZE Resizes the specified figure while keeping it on screen.\n% Usage:\n%   figsize([width, height])\n%   figsize(width, height)\n%   figsize % returns figure [width, height]\n%   figsize(h, ...)\n% \n% Args:\n%   h: handle of figure to resize (default: gcf)\n%   width: new width ([] = unchanged)\n%   height: new height ([] = unchanged)\n%\n% Returns:\n%   sz: [width, height] of the figure. Returns new size if output \n%       specified, if single figure handle specified, or if no args specified\n% \n% See also: movegui\n\nif nargin < 1; h = []; end\nif nargin < 2; width = []; end\nif nargin < 3; height = []; end\n\nargs = {h,width,height};\n\n% Figure out figure handle\nisfigarg = cellfun(@isfig,args);\nif any(isfigarg)\n    fig = args{isfigarg};\n    args(isfigarg) = [];\nelse\n    fig = gcf;\nend\n\n% Get current size\nfig_sz = fig.Position(3:4);\n\n% Validate inputs\nN = cellfun(@numel, args);\nassert(sum(N) <= 2, 'Invalid function syntax.')\n\n% Figure out new dimensions\nsz = fig_sz;\nif sum(N) == 2 % vector or 2 scalars specified\n    sz = [args{:}];\nelseif sum(N) == 1 % only 1 term specified\n    w = args{1};\n    h = args{2};\n    if numel(N) == 3 && N(1) < N(3) % {[], [], h} case\n        w = args{2};\n        h = args{3};\n    end\n    if isempty(w); w = fig_sz(1); end\n    if isempty(h); h = fig_sz(2); end\n    sz = [w,h];\nend\n\n\nif ~isequal(fig.Position(3:4),sz)\n    % Resize\n    fig.Position(3:4) = sz;\n\n    % Keep on screen\n    movegui(fig,'onscreen')\nend\n\n% Return new size if output specified, if single figure handle specified,\n% or if no args specified\nif ~(nargout > 0 || (sum(isfigarg) > 0 && nargin == 1) || nargin == 0)\n    clear sz\nend\n\nend\n"
  },
  {
    "path": "leap/toolbox/graphics/fontsize.m",
    "content": "function fontsize(size, h)\n%FONTSIZE Sets the fontsize across a graphics object.\n% Usage:\n%   fontsize(size) % default: gca\n%   fontsize(size, fig)\n%   fontsize(size, ax)\n% \n% See also: fontname\n\nif nargin < 2; h = gca; end\n\nset(findobj(h,'-property','FontSize'),'FontSize',size)\n\nend\n"
  },
  {
    "path": "leap/toolbox/graphics/hline.m",
    "content": "function h = hline(y, varargin)\n%HLINE Easy plotting of a horizonal line.\n% Usage:\n%   hline\n%   hline(y)\n%   hline(ax, _)\n%   hline(_, ...) % plot args (e.g., linespec)\n%   h = hline(_)\n% \n% Args:\n%   y: y-value\n% \n% See also: vline\n\nif nargin < 1; y = []; end\nax = [];\nif isax(y)\n    ax = y;\n    y = [];\n    if nargin > 1\n        arg1 = varargin{1};\n        if isnumeric(arg1)\n            y = arg1;\n            varargin(1) = [];\n        end\n    end\nend\n\nif isempty(ax)\n    ax = gca;\nend\n\nif isempty(y)\n    y = mean(ylim(ax));\nend\n\nstate = ax.NextPlot;\nax.NextPlot = 'add';\n\nX = xlim(ax) .* ones(numel(y),1);\nY = [1 1] .* y(:);\nif isvector(y)\n    X = [X NaN(size(X,1),1)]';\n    Y = [Y NaN(size(Y,1),1)]';\nend\n\nh = plot(ax, X(:), Y(:), varargin{:});\n\nax.NextPlot = state;\n\nif nargout < 1; clear h; end\n\nend\n"
  },
  {
    "path": "leap/toolbox/graphics/isax.m",
    "content": "function TF = isax(h)\n%ISAX Check if input is an axes handle.\n% Usage:\n%   TF = isax(h)\n% \n% Args:\n%   h: \n% \n% See also: isfig, isgraphics\n\nTF = isa(h,'matlab.graphics.axis.Axes');\n\nend\n"
  },
  {
    "path": "leap/toolbox/graphics/isfig.m",
    "content": "function TF = isfig(h)\n%ISFIG Checks whether the handle(s) specified are existing figures.\n% Usage:\n%   TF = isfig(h)\n%\n% See also: isax, isgraphics\n\n% Check if they are existing graphics object handles\nTF = ~isempty(h) && ishghandle(h);\n\n% Check if they are figures\nTF(TF) = strcmp(get(h(TF), 'type'), 'figure');\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/graphics/noticks.m",
    "content": "function noticks(ax, whichAxes)\n%NOTICKS Hides ticks in the axes.\n% Usage:\n%   noticks\n%   noticks('x') % hides only x-ticks\n%   noticks(ax, ...)\n\nif nargin == 1\n    if ischar(ax)\n        whichAxes = ax;\n        clear ax\n    end\nend\n\n% Defaults\nif ~exist('whichAxes', 'var'); whichAxes = 'xyz'; end\nif ~exist('ax', 'var'); ax = gca; end\n\nfor i = 1:numel(ax)\n    if any(whichAxes == 'x')\n        ax(i).XAxis.TickLength = [0 0];\n        ax(i).XAxis.TickLabelsMode = 'manual';\n        ax(i).XAxis.TickLabels = {};\n    end\n\n    if any(whichAxes == 'y')\n        ax(i).YAxis.TickLength = [0 0];\n        ax(i).YAxis.TickLabelsMode = 'manual';\n        ax(i).YAxis.TickLabels = {};\n    end\n\n    if any(whichAxes == 'z')\n        ax(i).ZAxis.TickLength = [0 0];\n        ax(i).ZAxis.TickLabelsMode = 'manual';\n        ax(i).ZAxis.TickLabels = {};\n    end\nend\nend\n\n"
  },
  {
    "path": "leap/toolbox/graphics/pareto2.m",
    "content": "function [ax, b, p] = pareto2(X, leftYLabel, rightYLabel)\n%PARETO2 Prettier pareto function.\n% Usage:\n%   pareto2(X)\n%   pareto2(X, leftYLabel, rightYLabel)\n%   [ax, b, p] = pareto2(...)\n%\n% See also: pareto, plotExplainedVar\n\n% Plot\n[ax, b, p] = plotyy(1:numel(X), X, 1:numel(X), cumsum(X), 'bar', 'plot');\n\n% Y axes labels\nif nargin >= 2; ylabel(ax(1), leftYLabel); end\nif nargin >= 3; ylabel(ax(2), rightYLabel); end\n\n% Make it prettier :)\np.LineWidth = 2.0;\naxis(ax, 'tight')\ngrid on\n\n% Fix Y ticks\nax(1).YTickMode = 'auto';\nax(2).YTickMode = 'auto';\n\nif nargout < 1\n    clear ax b p\nend\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/graphics/plotExplainedVar.m",
    "content": "function [ax, b, p] = plotExplainedVar(explained)\n%PLOTEXPLAINEDVAR Pretty plot PCA explained variance.\n% Usage:\n%   plotExplainedVar(explained)\n%   [ax, b, p] = plotExplainedVar(explained)\n%\n% See also: pareto2, plotyy\n\nif isstruct(explained) && isfield(explained,'explained'); explained = explained.explained; end\n\n[ax, b, p] = pareto2(explained, 'Component (%)', 'Cumulative (%)');\n% b.FaceColor = [0.85 0.325  0.098];\nb.FaceColor = [1 1 1] * 0.7;\n% b.FaceColor = min(b.FaceColor .* 1.2,1);\n% b.FaceAlpha = 0.4;\n% p.Color = [1 1 1] .* 0.4;\nlinkaxes(ax,'x')\nxlabel('Principal Components')\ntitle('PCA Explained Variance')\n\nfigclosekey\nif nargout < 1\n    clear ax b p\nend\nend\n\n"
  },
  {
    "path": "leap/toolbox/graphics/plotpts.m",
    "content": "function h = plotpts(pts, varargin)\n%PLOTPTS Convenience wrapper for plotting scatters. Same syntax as plot().\n% Usage:\n%   plotpts(pts)\n%   plotpts(pts, ...)\n%   h = plotpts(_)\n%\n% See also: plot, scatter\n\nif nargin < 2; varargin = {'.'}; end\n\nh = plot(pts(:,1), pts(:,2), varargin{:});\n\nif isempty(get(gcf,'KeyPressFcn'))\n    set(gcf,'KeyPressFcn',@KeyPressFcn_cb)\nend\n\nif nargout < 1; clear h; end\n\nend\n\nfunction KeyPressFcn_cb(h,evt)\nif strcmp(evt.Key,'q')\n    delete(h)\nend\nend"
  },
  {
    "path": "leap/toolbox/graphics/redblue.m",
    "content": "function map = redblue(N, dark)\n%REDBLUE Red and blue colormap going from blue to white to red.\n% Usage:\n%   map = redblue(N)\n% \n% Args:\n%   N: number of samples in the colormap (default: 64)\n%\n% Returns:\n%   map: N x 3 matrix of RGB colors\n%\n% Example:\n%   imagesc(repmat(linspace(-1,1,200),100,1)),colorbar,colormap redblue\n% \n% See also: colormap, parula\n\nif nargin < 1\n   f = get(groot,'CurrentFigure');\n   if isempty(f)\n      N = size(get(groot,'DefaultFigureColormap'),1);\n   else\n      N = size(f.Colormap,1);\n   end\nend\n\nif nargin < 2 || isempty(dark); dark = false; end\n\n% basis = [\n%     0 0 1 % blue\n%     1 1 1 % white\n%     1 0 0 % red\n%     ];\n\nmid_col = [1 1 1];\nif dark; mid_col = [0 0 0]; end\n\n% similar to redbluecmap (bioinformatics toolbox)\nbasis = [\n    0.0196078431372549         0.188235294117647         0.380392156862745 % blue\n    mid_col\n    0.403921568627451                         0          0.12156862745098 % red\n    ];\n\n\nP = size(basis,1);\nmap = interp1(1:size(basis,1), basis, linspace(1,P,N), 'linear');\n\nend\n"
  },
  {
    "path": "leap/toolbox/graphics/sc/gray.m",
    "content": "%GRAY  Black-white colormap\n%\n% Examples:\n%   map = gray;\n%   map = gray(len);\n%   B = gray(A);\n%   B = gray(A, lims);\n%\n% Similar to MATLAB's gray function, but also able to return a concise\n% colormap table.\n%\n% The function can additionally be used to convert a real-valued array into\n% a truecolor array using the colormap.\n%\n% IN:\n%   len - Scalar length of the output colormap. If len == Inf the concise\n%         table is returned. Default: len = size(get(gcf, 'Colormap'), 1);\n%   A - Non-scalar numeric array of real values to be converted into\n%       truecolor.\n%   lims - 1x2 array of saturation limits to be used on A. Default:\n%          [min(A(:)) max(A(:))].\n%\n% OUT:\n%   map - (len)xJ colormap table. J = 3, except in the concise case, when\n%         J = 4, map(1:end-1,4) giving the relative sizes of the \n%         inter-color bins.\n%   B - size(A)x3 truecolor array.\n\n% Copyright: Oliver Woodford, 2009\n\nfunction map = gray(varargin)\nmap = [0 0 0; 1 1 1];\nmap = colormap_helper(map, varargin{:});"
  },
  {
    "path": "leap/toolbox/graphics/sc/private/colormap_helper.m",
    "content": "function map = colormap_helper(map, len, lims)\n%COLORMAP_HELPER  Helper function for colormaps\n%\n% Examples:\n%   map = colormap_helper(map);\n%   map = colormap_helper(map, len);\n%   B = colormap_helper(map, A);\n%   B = colormap_helper(map, A, lims);\n%\n% Given a concise colormap table (i.e. one that contains all the\n% information required to create a full colormap, without any redundancy),\n% this function can return a colormap of the desired length, or convert a\n% real-valued array into truecolor array using the colormap.\n%\n% IN:\n%   map - KxJ colormap table. J = 3, except in the non-linear case, when\n%         J = 4, map(1:end-1,4) giving the relative sizes of the \n%         inter-color bins.\n%   len - Scalar length of the output colormap. If len == Inf the concise\n%         table is returned. Default: len = size(get(gcf, 'Colormap'), 1);\n%   A - Non-scalar numeric array of real values to be converted into\n%       truecolor.\n%   lims - 1x2 array of saturation limits to be used on A. Default:\n%          [min(A(:)) max(A(:))].\n%\n% OUT:\n%   map - (len)xJ colormap table. J = 3, except in the concise case, when\n%         J = 4, map(1:end-1,4) giving the relative sizes of the \n%         inter-color bins.\n%   B - size(A)x3 truecolor array.\n\n% $Id: colormap_helper.m,v 1.4 2009/04/13 12:16:22 ojw Exp $\n% Copyright: Oliver Woodford, 2009\n\nif nargin < 2\n   len = size(get(gcf, 'Colormap'), 1);\nend\nif isscalar(len)\n    if len == Inf\n        % Return the concise colormap table\n        return\n    end\n    len = 1:len;\n    sz = numel(len);\n    lims = [1 sz];\nelse\n    sz = size(len);\n    if nargin < 3\n        lims = [];\n    end\nend\nmap = reshape(real2rgb(len(:), map, lims), [sz 3]);"
  },
  {
    "path": "leap/toolbox/graphics/sc/private/rescale.m",
    "content": "function [B, lims] = rescale(A, lims, out_lims)\n%RESCALE  Linearly rescale values in an array\n%\n% Examples:\n%   B = rescale(A)\n%   B = rescale(A, lims)\n%   B = rescale(A, lims, out_lims)\n%   [B lims] = rescale(A)\n%\n% Linearly rescales values in an array, saturating values outside limits.\n%\n% IN:\n%   A - Input array of any size and class.\n%   lims - 1x2 array of saturation limits to be used on A. Default:\n%          [min(A(:)) max(A(:))].\n%   out_lims - 1x2 array of output limits the values in lims are to be\n%              rescaled to. Default: [0 1].\n%\n% OUT:\n%   B - size(A) double array.\n%   lims - 1x2 array of saturation limits used on A. Equal to the input\n%          lims, if given.\n\n% Copyright: Oliver Woodford, 2009 - 2011\n\n% 14/01/11 Fixed a bug brought to my attention by Ming Wu (Many thanks!).\n\nif nargin < 3\n    out_lims = [0 1];\nend\nif nargin < 2 || isempty(lims)\n    M = isfinite(A);\n    if ~any(reshape(M, numel(M), 1))\n        % All NaNs, Infs or -Infs\n        B = double(A > 0);\n        lims = [0 1];\n    else\n        lims = [min(A(M)) max(A(M))];\n        B = normalize(A, lims, out_lims);\n        B = min(max(B, out_lims(1)), out_lims(2));\n    end\n    clear M\nelse\n    B = normalize(A, lims, out_lims);\n    B = min(max(B, out_lims(1)), out_lims(2));\nend\nreturn\n\nfunction B = normalize(A, lims, out_lims)\nif lims(2) == lims(1) || out_lims(1) == out_lims(2)\n    B = zeros(size(A));\nelse\n    B = double(A);\n    if lims(1)\n        B = B - lims(1);\n    end\n    v = (out_lims(2) - out_lims(1)) / (lims(2) - lims(1));\n    if v ~= 1\n        B = B * v;\n    end\nend\nif out_lims(1)\n    B = B + out_lims(1);\nend\nreturn"
  },
  {
    "path": "leap/toolbox/graphics/sc/real2rgb.m",
    "content": "%REAL2RGB  Converts a real-valued matrix into a truecolor image\n%\n% Examples:\n%   B = real2rgb(A, cmap);\n%   B = real2rgb(A, cmap, lims);\n%   [B lims map] = real2rgb(...);\n%\n% This function converts a real-valued matrix into a truecolor image (i.e.\n% double array with values between 0 and 1) using the colormap specified\n% (either user-defined or the name of a colormap function). The output\n% image is suitable for display using IMAGE or IMSHOW, exporting using\n% IMWRITE, texture mapping a surface etc.\n%\n% Colormaps specified by name, e.g. 'hot', can be reversed ('-hot'), made\n% to convert linearly to grayscale when printed on a black & white printer\n% ('hot*'), or both ('-hot*').\n%\n% Value limits and a colormap table can be output, for use generating the\n% correct colorbar, e.g.:\n%   [B lims map] = real2rgb(peaks(256), '-hot*');\n%   hIm = imshow(B);\n%   set(gcf, 'Colormap', map);\n%   set(gca, 'CLim', lims);\n%   set(hIm, 'CDataMapping', 'scaled');\n%   colorbar;\n%\n% IN:\n%   A - MxN real matrix.\n%   cmap - JxK user-defined colormap, or a string indicating the name\n%          of the colormap to be used. K = 3 or 4. If K == 4 then\n%          cmap(1:end-1,4) contains the relative widths of the bins between\n%          colors. If cmap is a colormap function name then the prefix '-'\n%          indicates that the colormap is to be reversed, while the suffix\n%          '*' indicates that the colormap bins are to be rescaled so that\n%          each bin produces the same change in gray level, such that the\n%          colormap converts linearly to grayscale when printed in black\n%          and white.\n%   lims - 1x2 array of saturation limits to be used on A. Default:\n%          [min(A(:)) max(A(:))].\n%\n% OUT:\n%   B - MxNx3 truecolor image.\n%   lims - 1x2 array of saturation limits used on A. Same as input lims, if\n%          given.\n%   map - 256x3 colormap similar to that used to generate B.\n\n% Copyright: Oliver Woodford, 2009-2011\n\n% Thank you to Peter Nave for reporting a bug whereby colormaps larger than\n% 256 entries long are returned.\n\nfunction [B, lims, map] = real2rgb(A, cmap, lims)\n% Don't do much if A is wrong size\n[y, x, c] = size(A);\nif c > 1\n    error('A can only have 2 dimensions');\nend\nif y*x*c == 0\n    % Create an empty array with the correct dimensions\n    B = zeros(y, x, (c~=0)*3);\n    return\nend\nif nargin < 2\n    cmap = 'parula';\nend\n\n% Generate the colormap\nif ischar(cmap)\n    % If map starts with a '-' sign, invert the colormap\n    reverseMap = cmap(1) == '-';\n    % If the map ends with a '*', attempt to make map convert linearly to\n    % grayscale\n    grayMap = cmap(end) == '*';\n    % Extract the map name\n    cmap = lower(cmap(reverseMap+1:end-grayMap));\n    % Load the map\n    try\n        % Check for a concise table first\n        map = feval(cmap, Inf);\n    catch\n        map = [];\n    end\n    if invalid_map(map)\n        try\n            % Just load a large table\n            map = feval(cmap, 256);\n        catch\n            error('Colormap ''%s'' not found', cmap);\n        end\n        if invalid_map(map)\n            error('Invalid colormap');\n        end\n    end\n    if reverseMap\n        % Reverse the map\n        map = map(end:-1:1,:);\n        if size(map, 2) == 4\n            % Shift up the bin lengths\n            map(1:end-1,4) = map(2:end,4);\n        end\n    end\n    if grayMap && size(map, 1) > 2\n        % Ensure the map converts linearly to grayscale\n        map(1:end-1,4) = abs(diff(map(:,1:3) * [0.299; 0.587; 0.114]));\n    end\nelse\n    % Table-based colormap given\n    map = cmap;\nend\n\n% Only work with real doubles\nB = reshape(double(real(A)), y*x, c);\n\n% Compute limits and scaled values\nmaxInd = 1 + (size(map, 1) - 2) * (size(map, 2) ~= 4);\nif nargin < 3\n    lims = [];\nend\n[B, lims] = rescale(B, lims, [0 maxInd]);\n\n% Compute indices and offsets\nif size(map, 2) == 4\n    % Non-linear colormap\n    bins = map(1:end-1,4);\n    cbins = cumsum(bins);\n    bins(bins==0) = 1;\n    bins = cbins(end) ./ bins;\n    cbins = [0; cbins(1:end-1) ./ cbins(end); 1+eps];\n    [ind, ind] = histc(B, cbins);\n    B = (B - cbins(ind)) .* bins(ind);\n    clear bins cbins\nelse\n    % Linear colormap\n    ind = min(floor(B), maxInd-1);\n    B = B - ind;\n    ind = ind + 1;\nend\n\n% Compute the output image\ntry\n    B = bsxfun(@times, map(ind,1:3), 1 - B) + bsxfun(@times, map(ind+1,1:3), B);\ncatch\n    % If no bsxfun\n    B = B(:,[1 1 1]);\n    B = map(ind,1:3) .* (1 - B) + map(ind+1,1:3) .* B;\nend\nB = min(max(B, 0), 1); % Rounding errors can make values slip outside bounds\nB = reshape(B, y, x, 3);\n\nif nargout > 2 && (size(map, 1) ~= 256 || size(map, 2) == 4)\n    % Generate the colormap (for creating a colorbar with)\n    map = reshape(real2rgb(0:255, map, [0 255]), 256, 3);\nend\nreturn\n\nfunction notmap = invalid_map(map)\nnotmap = isempty(map) || ndims(map) ~= 2 || size(map, 1) < 1 || size(map, 2) < 3 || size(map, 2) > 4 || ~all(reshape(map(:,1:3) >= 0 & map(:,1:3) <= 1, [], 1));\n"
  },
  {
    "path": "leap/toolbox/graphics/shortticks.m",
    "content": "function shortticks(ax)\n%SHORTTICKS Make the axis ticks short.\n% Usage:\n%   shortticks\n%   shortticks(ax)\n% \n% See also: noticks\n\nif nargin < 1; ax = gca; end\n\nset(findobj(ax,'-property','TickLength'),'TickLength',[0 0])\n\nend\n"
  },
  {
    "path": "leap/toolbox/hdf5/h5att2struct.m",
    "content": "function S = h5att2struct(filename, location)\n%H5ATT2STRUCT Reads a set of HDF5 attributes into a named structure.\n% Usage:\n%   S = h5att2struct(filename, location)\n%\n% Returns:\n%   S: structure with fields corresponding to the attribute names, or empty\n%      if no attributes are found\n\nif nargin < 2; location = '/'; end\nif location(1) ~= '/'; location = ['/' location]; end\n\ninfo = h5info(filename, location);\n\nS = struct();\nif isempty(info.Attributes)\n    return\nend\n\n% Pull out data\nfieldnames = {info.Attributes.Name};\nvalues = {info.Attributes.Value};\n\n% Wrap cell arrays with {} to ensure they are scalar fields\nis_cell_arr = ~cellfun(@isscalar,values);\nvalues(is_cell_arr) = cf(@(x){x},values(is_cell_arr));\n\n% Create structure\nS = horz([horz(fieldnames); horz(values)]);\nS = struct(S{:});\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/hdf5/h5getdatasets.m",
    "content": "function datasets = h5getdatasets(filepath, grp, recurse)\n%H5GETDATASETS Returns a list of all datasets in an HDF5 file.\n% Usage:\n%   datasets = h5getdatasets(filepath)\n%   datasets = h5getdatasets(filepath, grp)\n% \n% Args:\n%   filepath: file path to HDF5 file\n%   grp: path to group within HDF5 file (default: '/')\n%   recurse: traverse subgroups to find datasets (default: true)\n%\n% Returns:\n%   datasets: cell array of paths to each dataset within the file\n%\n% See also: h5file, h5info\n\nif nargin < 2 || isempty(grp); grp = '/'; end\nif nargin < 3 || isempty(recurse); recurse = true; end\n\ninfo = h5info(filepath, grp);\ndatasets = getdsets(info, recurse);\n\nend\n\nfunction ds = getdsets(G, recurse)\n% Recurse through info structure and pull out datasets\n\nds = {};\nif ~isempty(G.Datasets)\n    base = G.Name;\n    if base(end) == '/'; base = base(1:end-1); end\n    ds = strcat(base, '/', {G.Datasets.Name})';\nend\n\nif recurse\n    for i = 1:numel(G.Groups)\n        ds = [ds; getdsets(G.Groups(i),recurse)];\n    end\nend\nend\n\n"
  },
  {
    "path": "leap/toolbox/hdf5/h5readframes.m",
    "content": "function [frames, numFrames] = h5readframes(filepath, dataset, idx)\n%H5READFRAMES Reads video frames from an HDF5 file.\n% Usage:\n%   frames = h5readframes(filepath)\n%   frames = h5readframes(filepath, idx)\n%   frames = h5readframes(filepath, dataset)\n%   frames = h5readframes(filepath, dataset, idx)\n%   [frames, numFrames] = h5readframes(_)\n%\n% See also: h5att2struct, h5struct2att, h5size\n\nif nargin < 2; dataset = []; end\nif nargin < 3; idx = []; end\n\nif (isnumeric(dataset) && ~isempty(dataset)) || ischar(idx)\n    tmp = idx;\n    idx = dataset;\n    dataset = tmp;\nend\n\nif isempty(dataset)\n    dataset = '/video/data';\n    if contains(filepath,'fg.h5'); dataset = '/fg'; end\n    if contains(filepath,'box'); dataset = '/box'; end\nend\n\nif dataset(1) ~= '/'\n    dataset = ['/' dataset];\nend\n\nif isempty(idx)\n    frames = h5read(filepath, dataset);\nelse\n    startFrame = min(idx);\n    endFrame = max(idx);\n\n    stride = 1;\n    if numel(idx) > 1\n        % Find largest stride that hits every frame\n        dFrames = diff(idx);\n        dFrames = sort(dFrames,'descend');\n        for stride = [horz(dFrames) 1]\n            if all(rem(dFrames,stride) == 0); break; end\n        end\n        \n        idx0 = idx; % requested indices\n        idx = startFrame:stride:endFrame; % indices to be read\n    end\n    sz = h5size(filepath, dataset);\n    if numel(sz) == 3\n        stride = [1 1 max([1 stride])];\n        start = [1 1 startFrame];\n        count = [inf inf numel(idx)];\n    else\n        stride = [1 1 1 max([1 stride])];\n        start = [1 1 1 startFrame];\n        count = [inf inf inf numel(idx)];\n    end\n    \n    frames = h5read(filepath, dataset, start, count, stride);\n    \n    if numel(idx) > 1\n        [~,idx_sub] = ismember(idx0,idx);\n        if ndims(frames) == 3\n            frames = frames(:,:,idx_sub);\n        else\n            frames = frames(:,:,:,idx_sub);\n        end\n    end\nend\nnumFrames = size(frames); numFrames = numFrames(end);\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/hdf5/h5readgroup.m",
    "content": "function data = h5readgroup(filepath, group)\n%H5READGROUP Reads a group into a structure containing all the data in the group.\n% Usage:\n%   data = h5readgroup(filepath, group)\n% \n% Args:\n%   filepath: path to the HDF5 file\n%   group: name or path to the group\n%\n% \n% \n% See also: h5read, h5save, h5att2struct\n\nif nargin < 2; group = '/'; end\nif group(1) ~= '/'; group = ['/' group]; end\n\ndata = struct();\ninfo = h5info(filepath, group);\n\nif ~isempty(info.Groups)\n    groups = {info.Groups.Name};\n    for i = 1:numel(groups)\n        grp_path = strsplit(groups{i},'/');\n        field_name = matlab.lang.makeValidName(grp_path{end});\n        data.(field_name) = h5readgroup(filepath,groups{i});\n    end\nend\n\nif ~isempty(info.Attributes)\n    data.Attributes = h5att2struct(filepath, group);\nend\n\nif ~isempty(info.Datasets)\n    datasets = {info.Datasets.Name};\n    for i = 1:numel(datasets)\n        dset_path = strsplit(datasets{i},'/');\n        field_name = matlab.lang.makeValidName(dset_path{end});\n        data.(field_name) = h5read(filepath, [group '/' datasets{i}]);\n        \n        if ~isempty(info.Datasets(i).Attributes)\n            data.Attributes.(datasets{i}) = h5att2struct(filepath, [group '/' datasets{i}]);\n        end\n    end\nend\n\nend\n"
  },
  {
    "path": "leap/toolbox/hdf5/h5save.m",
    "content": "function h5save(filepath, X, dset, varargin)\n%H5SAVE Create and save a variable to an HDF5 file.\n% Usage:\n%   h5save(filepath, X)\n%   h5save(filepath, X, dset)\n% \n% Args:\n%   filepath: path to HDF5 file\n%   X: variable to save\n%   dset: name or path to dataset (optional if variable name can be determined by inputname)\n%\n% Params:\n%   'compress': use GZIP compression filter for dataset (default: false)\n%   'chunking': shape to use for dataset chunking or dimension to use as singleton (default: [])\n%               if compress is true and this is not specified, chunking\n%               will default to the last dimension of the dataset\n% \n% See also: h5write, h5att2struct, h5struct2att, inputname\n\nif nargin < 3 || isempty(dset)\n    dset = inputname(2);\n    if isempty(dset)\n        error('Dataset name must be specified if not saving a named variable.')\n    end\nend\n\n% Make sure dataset starts with '/'\nif dset(1) ~= '/'; dset = ['/' dset]; end\n\n% Parse ptional arguments:\ndefaults = struct('compress',false,'chunking',[]);\n[params, unmatched] = parse_params(varargin, defaults);\nif isfield(unmatched, 'ChunkSize'); params.chunking = unmatched.ChunkSize; end\nif isfield(unmatched, 'Deflate'); params.compress = unmatched.Deflate; end\n\nif params.compress > 0; params.compress = double(params.compress); end\n\n% If compression is enabled, use last dimension as chunking dim\nif isempty(params.chunking) && params.compress > 0\n    params.chunking = ndims(X);\nend\n\n% If chunking is specified as dimension instead of full size, expand to chunk size\nif isscalar(params.chunking)\n    d = params.chunking;\n    params.chunking = size(X);\n    params.chunking(d) = 1;\nend\n\n% Infer data type from input\ndtype = class(X);\nif islogical(X)\n    X = uint8(X);\nend\n\n% Exceptions identifiers:\n% MATLAB:imagesci:h5write:datasetDoesNotExist\n% MATLAB:imagesci:h5create:datasetAlreadyExists\ntry\n    args = {};\n    if ~isempty(params.chunking); args = [args, {'ChunkSize', params.chunking}]; end\n    if params.compress; args = [args, {'Deflate', params.compress}]; end\n    h5create(filepath, dset, size(X), 'Datatype', class(X), args{:})\n    h5writeatt(filepath, dset, 'dtype', dtype)\ncatch ME\n    switch ME.identifier\n        case 'MATLAB:imagesci:h5create:datasetAlreadyExists'\n            warning('h5save:overwrite','Overwriting existing dataset: %s', dset)\n        otherwise\n            rethrow(ME)\n    end\nend\n\nh5write(filepath, dset, X)\n\nend\n"
  },
  {
    "path": "leap/toolbox/hdf5/h5savegroup.m",
    "content": "function h5savegroup(filepath, S, grp, varargin)\n%H5SAVEGROUP Saves a struct as a HDF5 group.\n% Usage:\n%   h5savegroup(filepath, S, grp)\n% \n% Args:\n%   filepath: path to HDF5 file to save\n%   S: structure to save as an HDF5 group\n%   grp: path to group (default: '/[S]' where [S] is the variable name of the struct)\n%\n% Params: see h5save\n% \n% See also: h5readgroup, h5save\n\nif nargin < 3; grp = inputname(2); end\nif isempty(grp); grp = ''; end\nif ~startsWith(grp,'/'); grp = ['/' grp]; end\n\nfns = fieldnames(S);\nisAttr = strcmp(fns,'Attributes');\nfns = [fns(~isAttr); fns(isAttr)]; % move attributes to last so file is created\nfor i = 1:numel(fns)\n    dset = [grp '/' fns{i}];\n    if isstruct(S.(fns{i}))\n        if strcmp(fns{i},'Attributes')\n            h5struct2att(filepath, grp, S.(fns{i}))\n        else\n            h5savegroup(filepath, S.(fns{i}), dset, varargin{:})\n        end\n    else\n        try\n            h5save(filepath, S.(fns{i}), dset, varargin{:})\n        catch\n            warning('Failed to save dataset: %s', dset)\n        end\n    end\nend\n\nend\n"
  },
  {
    "path": "leap/toolbox/hdf5/h5size.m",
    "content": "function [sz, maxSize] = h5size(filepath, dataset, dim)\n%H5SIZE Returns the size of the specified dataset.\n% Usage:\n%   [size, maxSize] = h5size(filepath, dataset)\n%   [size, maxSize] = h5size(filepath, dataset, dim)\n\nif nargin < 3; dim = []; end\n\nif nargout > 1\n    info = h5info(filepath, dataset);\n\n    sz = info.Dataspace.Size;\n    maxSize = info.Dataspace.MaxSize;\n\n    if ~isempty(dim)\n        sz = sz(dim);\n        maxSize = maxSize(dim);\n    end\nelse\n    sz = size(h5file(filepath, dataset));\n    if ~isempty(dim)\n        sz = sz(dim);\n    end\nend\nend\n\n"
  },
  {
    "path": "leap/toolbox/hdf5/h5struct2att.m",
    "content": "function h5struct2att(filepath, location, S)\n%H5STRUCT2ATT Writes attributes to an HDF5 file from a scalar structure.\n% Usage:\n%   h5struct2att(filepath, location, S)\n%\n% See also: h5att2struct\n\nif nargin == 2 && isstruct(location); S = location; location = inputname(2); end\nif isempty(location); location = ''; end\nif ~startsWith(location,'/'); location = ['/' location]; end\n\nnames = fieldnames(S);\nfor i = 1:numel(names)\n    if islogical(S.(names{i})); S.(names{i}) = uint8(S.(names{i})); end\n    if iscellstr(S.(names{i})); S.(names{i}) = strjoin(S.(names{i}),'\\n'); end\n    if isstruct(S.(names{i})); continue; end % TODO: recursive group attributes\n    h5writeatt(filepath, location, names{i}, S.(names{i}))\nend\n\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/hdf5/hdf5prop/h5datacreate.m",
    "content": "function datasetId = h5datacreate(h5file,varname,varargin)\n%H5datacreate  Create HDF5 dataset.\n%\n%   H5DATACREATE(HFILE,VARNAME,parameter,value,...) creates a dataset\n%   named VARNAME.  HFILE may be either a path to the HDF5 file or a file \n%   ID to an already opened file.  \n%\n%   If VARNAME is a full pathname, all \n%   intermediate groups are created if they don't already exist.\n%\n%   parameter\n%     'size'        - size of the dataset\n%     'chunk_size'  - size of the chunks of the dataset\n%     'compression' - 0-9  compression level\n%     'type'        - type of the dataset to create.  This can be a string\n%                     such as 'double', in which case the datatype maps to\n%                     'H5T_NATIVE_DOUBLE', or it can be a derived HDF5 \n%                     datatype.\n%     'max_size'    - the maximum size of the dataset to create\n%     'fill'        - the fill value\n%\n%\n%   Example:  create a 10x20 single precision dataset named 'DS1'.\n%       h5filecreate('myfile.h5');\n%       h5datacreate('myfile.h5','DS1','type','single','size',[10 20]);\n%\n%   Example:  create a 4x1 string dataset where each string has length\n%   8 and is null-terminated.\n%       mytype = H5T.copy('H5T_C_S1');\n%       H5T.set_size(mytype,8);\n%       H5T.set_strpad(mytype,'H5T_STR_NULLTERM');\n%       h5datacreate('myfile.h5','/path/to/DS2','type',mytype,'size',4);\n%\n%   Credit where credit is due:  Philip Top at LLNL\n%\n%   See also h5filecreate.\n\n%   Copyright 2010 The MathWorks, Inc.\n\np = inputParser;\np.addParamValue('size',0,@isnumeric);\np.addParamValue('chunk_size',0,@isnumeric);\np.addParamValue('compression',0,@isnumeric);\np.addParamValue('type',H5T.copy('H5T_NATIVE_DOUBLE'),@(x)ischar(x) || isa(x,'H5ML.id'));\np.addParamValue('max_size',0,@isnumeric);\np.addParamValue('fill',0,@isnumeric);\np.parse(varargin{:});\nparams = p.Results;\nparams = validate_params(h5file,varname,params);\n\n\nflags = 'H5F_ACC_RDWR';\nfapl  = 'H5P_DEFAULT'; \n\nif ischar(h5file)\n    file_id = H5F.open(h5file,flags,fapl);\nelse\n    file_id=h5file;\nend\n\n\n\nparams = set_dataspace_id (params);\nparams = set_datatype_id (params);\nparams = set_dcpl(params);\n\ndatasetId = set_data_id(file_id,varname,params);\n\nH5S.close(params.dataspaceId);\nH5T.close(params.datatypeId);\nH5P.close(params.dcpl);\n\n\nif ischar(h5file)\n    H5F.close(file_id);\nend\n\n\n\n\n\n%==========================================================================\nfunction params = set_dcpl(params)\n% Setup the dataset creation property list.  \n\nparams.dcpl = H5P.create('H5P_DATASET_CREATE'); % create property list\nif (~isequal(params.chunk_size,0))\n    % set chunk size\n    H5P.set_chunk(params.dcpl,fliplr(params.chunk_size)); \nend\nif (params.compression~=0)\n    % set gzip compression level\n    H5P.set_deflate(params.dcpl,params.compression);\nend\nif (params.fill~=0)\n    % set fill value\n    H5P.set_fill_value(params.dcpl, params.datatypeId, params.fill.*ones(params.type));\nend\nreturn\n\n\n\n\n\n\n\n \n%==========================================================================\n% SET_DATASET_ID\n%\n% Setup the dataet ID.  We need to check as to whether or not the dataset\n% already exists.\nfunction datasetID = set_data_id(fid,dataname,params)\n\nv = version('-release');\nswitch(v)\n\tcase { '2007b', '2008a', '2008b' }\n\t\t% Give it our best shot.\n\t\tdatasetID = H5D.create(fid,dataname,...\n                       params.datatypeId, params.dataspaceId, ...\n                       params.dcpl);\n\n\totherwise\n\t\n\t\t% create any intermediate groups along the way.\n\t\tparams.lcpl = H5P.create('H5P_LINK_CREATE');\n\t\tH5P.set_create_intermediate_group(params.lcpl,1);\n\t\tdapl = 'H5P_DEFAULT';\n\n\n\t\t% need to use the 1.8.x version of H5D.create for this.\n\t\tdatasetID = H5D.create(fid,dataname,...\n                       params.datatypeId, params.dataspaceId, ...\n                       params.lcpl,params.dcpl,dapl);\n\nend\n\nreturn\n\n%===============================================================================\n% SET_DATASPACE_ID\n%\n% Setup the dataspace ID.\n\nfunction params = set_dataspace_id (params)\n\nparams.dataspaceId=H5S.create('H5S_SIMPLE');\nH5S.set_extent_simple(params.dataspaceId,length(params.size),...\n    fliplr(params.size),fliplr(params.max_size));\nreturn\n\n\n\n%===============================================================================\n% SET_DATATYPE_ID\n%\n% We need to choose an appropriate HDF5 datatype\nfunction params = set_datatype_id (params)\nif ischar(params.type)\n    switch params.type\n        case 'double'\n            params.datatypeId = H5T.copy('H5T_NATIVE_DOUBLE');\n        case {'single','float'}\n            params.datatypeId = H5T.copy('H5T_NATIVE_FLOAT');\n        case 'int64'\n            params.datatypeId = H5T.copy('H5T_NATIVE_LLONG');\n        case 'uint64'\n            params.datatypeId = H5T.copy('H5T_NATIVE_ULLONG');\n        case {'int32','int'}\n            params.datatypeId = H5T.copy('H5T_NATIVE_INT');\n        case {'uint32','uint'}\n            params.datatypeId = H5T.copy('H5T_NATIVE_UINT');\n        case {'int16','short'}\n            params.datatypeId = H5T.copy('H5T_NATIVE_SHORT');\n        case 'uint16'\n            params.datatypeId = H5T.copy('H5T_NATIVE_USHORT');\n        case 'int8'\n            params.datatypeId = H5T.copy('H5T_NATIVE_SCHAR');\n        case 'uint8'\n            params.datatypeId = H5T.copy('H5T_NATIVE_UCHAR');\n        case 'complex'\n            % special case for complex data.  We'll actually create 3 \n            % datasets.  One to hold the real data, one to hold the \n            % complex data, and one to hold references to both.\n            params.datatypeId = H5T.copy('H5T_STD_REF_OBJ');\n        otherwise\n            error('hdf5tools:H5DATACREATE:unsupportedDatatype', ...\n                '''%s'' is not a supported H5DATACREATE datatype.\\n', params.type );\n    end\n    return\nend\n\nif isa(params.type,'H5ML.id')\n    params.datatypeId = H5T.copy(params.type);\n    return\nend\n\n\n\n%--------------------------------------------------------------------------\nfunction params = validate_params(hfile,varname,params)\n\nif (params.size == 0 )\n    error('hdf5tools:h5datacreate:zeroSize', ...\n        'Size must be specified and be greater than zero.');\nend\n\n\nif ~ischar(hfile)\n    if ~isequal(class(hfile),'H5ML.id')\n        error('hdf5tools:H%DATACREATE:badDatatype', ...\n            'Filename input argument must have datatype char.' );\n        \n    end\nend\n\nif ~ischar(varname)\n    error('hdf5tools:H5DATACREATE:badDatatype', ...\n        'VARNAME input argument must have datatype char.' );\nend\n\n\n\nif (~isequal(params.chunk_size,0))\n    if (numel(params.chunk_size)~=numel(params.size))\n        error('dataset size and chunk size have different dimensions');\n    end\nend\n\nif (~isequal(params.max_size,0))\n    if (isequal(params.chunk_size,0))\n        error('chunk size must be specified if max size is different from size')\n    end\n    if (numel(params.max_size)~=numel(params.size))\n        error('dataset max size and initial size have different dimensions');\n    elseif (any(isinf(params.max_size)))\n        mxs=cell(size(params.max_size));\n        for kk=1:length(params.max_size)\n            if (isinf(params.max_size(kk)))\n                mxs{kk}='H5S_UNLIMITED';\n            else\n                mxs{kk}=params.max_size(kk);\n            end\n        end\n        params.max_size = mxs;\n    end\n        \nelse\n    params.max_size=params.size;\nend\n\nif ((params.compression<0)||(params.compression>9))\n    error('invalid compression level');\n    \nelse\n    if (params.compression>0)\n        if (isequal(params.chunk_size,0))\n            params.chunk_size=params.size;\n        end\n    end\nend\nreturn\n\n\n\n\n\n\n\n"
  },
  {
    "path": "leap/toolbox/hdf5/hdf5prop/hdf5prop.m",
    "content": "classdef hdf5prop < handle\n% HDF5PROP Class for transparent file data access.\n% Matlab class to create and access HDF5 datasets transparently as Matlab\n% variable. Data can be accessed and written with subscript referencing and\n% assignment methods, just like a matlab variable, only size must be\n% explicitly set or changed.\n%\n%   prop = hdf5prop(file, dataset)\n%     Creates a 'hdf5prop' object for the given dataset in the given HDF5\n%     file.\n%\n%   prop = hdf5prop(file, dataset, mode)\n%     Specify the access mode, \n%       mode = 'r' : readonly access (default)\n%       mode = 'rw': readwrite access\n%\n%   prop = hdf5prop(file, dataset, parameter, value, ...)\n%     Creates a 'hdf5prop' object for a new dataset with given parameters.\n%\n%     parameter\n%       'size'        - size of the dataset. Necessary.\n%       'chunk_size'  - size of the chunks of the dataset.\n%       'compression' - 0-9  compression level.\n%       'type'        - type of the dataset to create.  This can be a string\n%                       such as 'double', in which case the datatype maps to\n%                       'H5T_NATIVE_DOUBLE', or it can be a derived HDF5 \n%                       datatype.\n%       'max_size'    - the maximum size of the dataset to create.\n%       'fill'        - the fill value.\n\n% copyleft 2011, Piers Titus van der Torren\n\n  % TODO: replace all H5 calls to direct mex calls for speed\n  properties( SetAccess = protected )\n    file\n    dataset\n    mode = 'H5F_ACC_RDONLY'\n    propsize\n  end\n  properties( SetAccess = protected, Transient = true )\n    dataset_id\n  end\n  \n  methods\n    function self = hdf5prop(file,dataset,varargin)\n      \n      self.file = file;\n      self.dataset = dataset;\n      if nargin>2\n        switch varargin{1}\n          case {'H5F_ACC_RDWR','rw'}\n            self.mode = 'H5F_ACC_RDWR';\n            varargin = varargin(2:end);\n          case {'H5F_ACC_RDONLY','r'}\n            varargin = varargin(2:end);\n          otherwise            \n        end\n      end\n      if ~exist(file, 'file')\n        % create file\n        file_id = H5F.create(file, 'H5F_ACC_EXCL', 'H5P_DEFAULT', 'H5P_DEFAULT');\n        self.mode = 'H5F_ACC_RDWR';\n      else\n        file_id = H5F.open(self.file, self.mode, 'H5P_DEFAULT');\n      end\n      try\n        self.dataset_id = H5D.open(file_id,self.dataset);\n        if ~isempty(varargin)\n          error('hdf5prop:exists', 'dataset already exists, even though creation parameters are given')\n        end\n      catch ex\n        if strcmp(ex.identifier, 'hdf5prop:exists')\n          rethrow(ex);\n        end\n        % create dataset\n        \n        if strcmp(self.mode, 'H5F_ACC_RDONLY')\n          self.mode = 'H5F_ACC_RDWR';\n          H5F.close(file_id);\n          file_id = H5F.open(self.file, self.mode, 'H5P_DEFAULT');\n        end\n        \n        self.dataset_id = h5datacreate(file_id, self.dataset, varargin{:});\n        \n      end\n      \n      space = H5D.get_space(self.dataset_id);\n      [ans, dims] = H5S.get_simple_extent_dims(space);\n      self.propsize = fliplr(dims);\n    end\n    \n    function open( self )\n      if ~isa(self.dataset_id, 'H5ML.id') || self.dataset_id.identifier < 0\n        \n        file_id=H5F.open(self.file,self.mode,'H5P_DEFAULT');\n        self.dataset_id=H5D.open(file_id,self.dataset);\n\n        % get size of dataset\n        % TODO: size is not valid when file is changed elsewhere while open\n        space = H5D.get_space(self.dataset_id);\n        [ndims, dims] = H5S.get_simple_extent_dims(space);\n        self.propsize = fliplr(dims);\n      end\n    end\n    \n    function close( self )\n      % CLOSE Close file if open.\n      if isa(self.dataset_id, 'H5ML.id') \n        H5D.close(self.dataset_id);\n      end\n    end\n    \n    function set_extent( self, sz)\n      % SET_EXTENT Allocate space\n      %\n      %   hdf5prop.set_extent(sz)\n      %     Extend allocated space to size sz.\n      self.open();\n      H5D.extend(self.dataset_id, fliplr(sz));\n      %H5D.set_extent(self.dataset_id, fliplr(sz));\n      self.propsize = sz;\n    end\n    \n    function set_mode( self, mode )\n      self.close()\n      switch mode\n        case {'H5F_ACC_RDWR','rw'}\n          self.mode = 'H5F_ACC_RDWR';\n        case {'H5F_ACC_RDONLY','r'}\n          self.mode = 'H5F_ACC_RDONLY';\n        otherwise\n          error('unknown mode')\n      end\n      self.open()\n    end\n    \n    function lock( self )\n      self.close()\n      self.mode = 'H5F_ACC_RDONLY';\n      self.open()\n    end\n  end\n  \n  methods( Hidden=true )  \n    \n    function varargout = subsref(self, S)\n      switch S(1).type\n        case {'.', '{}'}\n          [varargout{1:nargout}] = builtin('subsref',self,S);\n        case '()'\n          [varargout{1:nargout}] = self.get_data(S(1).subs{:});\n%         case '{}'\n%           error('{} not allowed')\n      end\n    end\n    \n    function self = subsasgn(self, S, B)\n      switch S(1).type\n        case {'.', '{}'}\n          builtin('subsasgn',self,S,B);\n        case '()'\n          self.set_data(S(1).subs, B);\n%         case '{}'\n%           error('{} not allowed')\n      end\n    end\n    \n    function out = get_data(self, varargin)\n      \n      self.open()\n      \n      % return all data\n      if nargin == 1 || all(strcmp(varargin,':'))\n        out = H5D.read(self.dataset_id,'H5ML_DEFAULT','H5S_ALL','H5S_ALL','H5P_DEFAULT');\n        return;\n      end\n      \n      outsize = [];\n      \n      propsize = size(self);\n      dim = numel(propsize);\n      % fill singular dimensions if there's only 1 nonsingular dimension\n      if dim > 1 && numel(varargin)==1 && sum(propsize ~= 1) <= 1\n        a = varargin;\n        varargin = repmat({':'},1,dim);\n        if any(propsize ~= 1)\n          varargin{propsize ~= 1} = a{1};\n        end\n        outsize = size(a{1});\n        if sum(outsize~=1)==1\n          outsize = [];\n        end\n      end\n      assert(dim == numel(varargin),'linear or multidimensional boolean indexing is not yet supported, now use all %d dimensions',dim);\n      \n      % divide selection into hyperslabs\n      start = nan(1,dim);\n      count = nan(1,dim);\n      stride = nan(1,dim);\n      hyperslab = true;\n      for n = 1:dim\n        sel = varargin{dim+1-n};  % traverse reversed instead of fliplr\n        if isa(sel,'char') && sel == ':'\n          start(n) = 1;\n          count(n) = propsize(dim+1-n);\n          stride(n) = 1;\n          idxc{n} = sel;\n          selc{n} = sel;\n        else\n          %if isempty(sel)\n          %  % if one dimension is empty the result is empty too\n          %  out = [];\n          %  return\n          %end\n          \n          sel = sel(:);\n          if isa(sel,'logical')\n            sel = find(sel);\n            idxc{n} = ':';\n          elseif numel(sel) <= 1 || ( issorted(sel) && min(diff(sel))>0 )\n            idxc{n} = ':';\n          else\n            [sel, ans, idxc{n}] = unique(sel);\n          end\n          \n          selc{n} = sel;\n          \n          count(n) = numel(sel);\n\n          dsel = diff(sel);\n          if isempty(sel)\n            start(n) = 1;\n            stride(n) = 1;\n          elseif numel(sel)==1\n            start(n) = sel(1);\n            stride(n) = 1;\n          elseif all(dsel == dsel(1))\n            start(n) = sel(1);\n            stride(n) = dsel(1);\n          else\n            hyperslab = false;\n          end\n        end\n      end\n\n      % TODO: fix datatype \n      if any(count==0)\n        out = zeros(fliplr(count));\n        return\n      end\n      \n      space=H5D.get_space(self.dataset_id);\n      if hyperslab\n        %H5ML.hdf5lib2('H5Sselect_hyperslab',space_double,'H5S_SELECT_SET',start,stride,count,[]);\n        H5S.select_hyperslab(space,'H5S_SELECT_SET',start-1,stride,count,[]);\n      else\n        s = find(isnan(start));\n        selend = cellfun(@(x) x(end),selc(s));\n        sel1 = cellfun(@(x) x(1),selc(s));\n        if prod(count(s)) > .1 * prod(selend - sel1)\n          % since hyperslab is way faster than select_elements, it is\n          % faster to read a full hypeslab and do the selection in\n          % matlab. The factor above should match the speed ratio.\n          start(s) =  sel1;\n          stride(s) = 1;\n          count(s) = selend-sel1+1;\n          for n=s\n            idxc{n} = selc{n}(idxc{n}) - selc{n}(1) + 1;\n          end\n          H5S.select_hyperslab(space,'H5S_SELECT_SET',start-1,stride,count,[]);\n        else\n          sel = zeros(dim,prod(count));\n          % ndgrid of selc{:}\n          for n=1:dim,\n            if ischar(selc{n})\n              x = (1:count(n))';\n            else\n              x = selc{n}; % Extract and reshape as a vector.\n            end\n            s = count; s(n) = []; % Remove i-th dimension\n            x = reshape(x(:,ones(1,prod(s))),[length(x) s]); % Expand x\n            x = permute(x,[2:dim-n+1 1 dim-n+2:dim]); % Permute to i'th dimension\n            sel(n,:) = x(:);\n          end\n          %warning('using H5S.select_elements, might be slow')\n          H5S.select_elements(space,'H5S_SELECT_SET',sel-1);\n        end\n      end\n      \n      % reserve space for reading\n      mem_space = H5S.create_simple(dim, count, []);\n\n      % read the actual data\n      out = H5D.read(self.dataset_id,'H5ML_DEFAULT',mem_space,space,'H5P_DEFAULT');\n      \n      % shuffle output to match selection\n      out=out(idxc{end:-1:1});\n      \n      if ~isempty(outsize)\n        out = reshape(out,outsize);\n      end\n\n      H5S.close(space);\n      H5S.close(mem_space);\n    end\n    \n    function set_data(self, argin, B)\n      \n      if ~strcmp(self.mode, 'H5F_ACC_RDWR')\n        error('this hdf5prop is read only')\n      end\n      \n      self.open()\n      \n      propsize = size(self);\n      dim = numel(propsize);\n      % fill singular dimensions if there's only 1 nonsingular dimension\n      if dim > 1 && numel(argin)==1 && sum(propsize ~= 1) <= 1\n        a = argin;\n        argin = repmat({':'},1,dim);\n        if any(propsize ~= 1)\n          argin{propsize ~= 1} = a{1};\n        end\n      end\n      assert(dim == numel(argin),'linear or multidimensional boolean indexing is not yet supported, now use all %d dimensions',dim);\n      \n      colondim = strcmp(argin,':');\n      \n      % get input indexing dimensions\n      indim = 1:dim;\n      for n=1:dim\n        if colondim(n)\n          indim(n) = propsize(n);\n        elseif isa(argin{n},'logical')\n          indim(n) = sum(argin{n}(:));\n        else\n          indim(n) = numel(argin{n});\n        end\n      end\n      \n      % check if squeezed dimensions match\n      indim = max(cellfun(@numel,argin), colondim.*propsize);\n      szB = size(B);\n      assert(numel(B)==1 || all(indim(indim~=1)==szB(szB~=1)),'Subscripted assignment dimension mismatch.')\n      \n      if any(indim==0)\n        % nothing to be done\n        return;\n      end\n      \n      % unsqueeze input\n      if numel(B)~=1\n        B = reshape(B,indim);\n      end\n      \n      % write all data\n      if all(colondim)\n        if numel(B) == 1\n          B = repmat(B,propsize);\n          % H5D.fill ?\n        end\n        \n        H5D.write(self.dataset_id,'H5ML_DEFAULT','H5S_ALL','H5S_ALL','H5P_DEFAULT',B);\n        return;\n      end\n      \n      % divide selection into hyperslabs\n      start = zeros(1,dim);\n      count = zeros(1,dim);\n      stride = zeros(1,dim);\n      hyperslab = true;\n      for n = 1:dim\n        sel = argin{dim+1-n};  % traverse reversed instead of fliplr\n        if isa(sel,'char') && sel == ':'\n          start(n) = 1;\n          count(n) = propsize(dim+1-n);\n          stride(n) = 1;\n          idxc{n} = sel;\n          selc{n} = sel;\n        else\n          sel = sel(:);\n          if isa(sel,'logical')\n            sel = find(sel);\n            idxc{n} = ':';\n          elseif numel(sel) == 1 || ( issorted(sel) && min(diff(sel))>0 )\n            idxc{n} = ':';\n          else\n            % this line is different from get_data\n            [sel, idxc{n}] = unique(sel);\n          end\n          \n          selc{n} = sel;\n          \n          count(n) = numel(sel);\n          if hyperslab\n            dsel = diff(sel);\n            if numel(sel)==1\n              start(n) = sel(1);\n              stride(n) = 1;\n            elseif all(dsel == dsel(1))\n              start(n) = sel(1);\n              stride(n) = dsel(1);\n            else\n              hyperslab = false;\n            end\n          end\n        end\n      end\n      \n      space=H5D.get_space(self.dataset_id);\n      if hyperslab\n        %H5ML.HDF5lib2('H5Sselect_hyperslab',space_double,'H5S_SELECT_SET',start,stride,count,[]);\n        H5S.select_hyperslab(space,'H5S_SELECT_SET',start-1,stride,count,[]);\n      else\n        sel = zeros(dim,prod(count));\n        % ndgrid of selc{:}\n        for n=1:dim,\n          if ischar(selc{n})\n            x = (1:count(n))';\n          else\n            x = selc{n}; % Extract and reshape as a vector.\n          end\n          s = count; s(n) = []; % Remove i-th dimension\n          x = reshape(x(:,ones(1,prod(s))),[length(x) s]); % Expand x\n          x = permute(x,[2:dim-n+1 1 dim-n+2:dim]); % Permute to i'th dimension\n          sel(n,:) = x(:);\n        end\n        H5S.select_elements(space,'H5S_SELECT_SET',sel-1);\n      end\n      \n      % reserve space for reading\n      mem_space = H5S.create_simple(dim, count, []);\n\n      % prepare input\n      if prod(count) > 1\n        if numel(B) == 1\n          B = repmat(B,count);\n        else\n          % shuffle input to match selection\n          B = B(idxc{end:-1:1});\n        end\n      end\n      \n      % write the actual data\n      H5D.write(self.dataset_id,'H5ML_DEFAULT',mem_space,space,'H5P_DEFAULT',B);\n      \n      H5S.close(space);\n      H5S.close(mem_space);\n    end\n    \n    function varargout = size(self, dim)\n      self.open()\n      sz = self.propsize;\n      if nargout > 1\n        varargout = num2cell(sz);\n      elseif nargin > 1\n        varargout = {sz(dim)};\n      else\n        varargout = {sz};\n      end\n    end\n    \n    % overloading numel causes problems with builtin subsref\n%     function out = numel(self)\n%       out = 1;%prod(size(self));\n%     end\n    \n    function out = length(self)\n      out = max(size(self));\n    end\n    \n    function out = end(self, dim, n_dim)\n      out = size(self,dim);\n    end\n    \n    function out = vertcat(varargin)\n      error('array of hdf5prop objects is not allowed')\n    end\n    \n    function out = horzcat(varargin)\n      error('array of hdf5prop objects is not allowed')\n    end\n    \n    function out = cat(varargin)\n      error('array of hdf5prop objects is not allowed')\n    end\n    \n    function out = double( self )\n      out = double(self.get_data());\n    end\n    \n    function out = eq( self, other )\n      out = eq(self.get_data(), other());\n    end\n    \n    function out = ne( self, other )\n      out = ne(self.get_data(), other());\n    end\n    \n    function out = le( self, other )\n      out = le(self.get_data(), other());\n    end\n    \n    function out = ge( self, other )\n      out = qe(self.get_data(), other());\n    end\n    \n    function out = lt( self, other )\n      out = lt(self.get_data(), other());\n    end\n    \n    function out = gt( self, other )\n      out = gt(self.get_data(), other());\n    end\n  end\nend\n"
  },
  {
    "path": "leap/toolbox/imageproc/ind2im.m",
    "content": "function I = ind2im(ind, sz, vals, fillval)\n%IND2IM Create image from a set of linear indices.\n% Usage:\n%   I = ind2im(ind, sz)\n%   I = ind2im(ind, sz, vals)\n%   I = ind2im(ind, sz, vals, fillval)\n%   I = ind2im(BW, vals)\n%   I = ind2im(BW, vals, fillval)\n%   I = ind2im(S)\n% \n% Args:\n%   ind: linear indices of the image\n%   sz: size of the output image\n%   vals: values to fill in with (default: true)\n%   S: structure with the fields 'sz, 'vals' and 'ind'\n%   fillval: value to fill the rest of the array with\n%\n% Output:\n%   I: image of size sz, the same type as vals, and values filled in at ind\n% \n% See also: im2ind, mask2im, ind2sub\n\nif nargin < 4 || isempty(fillval); fillval = 0; end\nif nargin < 3 || isempty(vals); vals = []; end\nif nargin >= 2 && islogical(ind) && numel(sz) == sum(ind(:))\n    if nargin == 3; fillval = vals; end\n    vals = sz;\n    sz = size(ind);\nend\nif nargin == 1 && isstruct(ind)\n    if isfield(ind,'sz'); sz = ind.sz; end\n    if isfield(ind,'size'); sz = ind.size; end\n    \n    if isfield(ind,'val'); vals = ind.val; end\n    if isfield(ind,'vals'); vals = ind.vals; end\n    \n    if isfield(ind,'ind'); ind = ind.ind; end\n    if isfield(ind,'idx'); ind = ind.idx; end\nend\nif isempty(vals); vals = true; end\n\n\nI = zeros(sz, 'like', vals);\nI(:) = fillval;\nI(ind) = vals;\n\nend\n"
  },
  {
    "path": "leap/toolbox/inputParsing/get_caller_name.m",
    "content": "function caller = get_caller_name(varargin)\n%GET_CALLER_NAME Returns the name of the caller function.\n% Usage:\n%   caller = get_caller_name()\n%   caller = get_caller_name('Path', true)\n%   caller = get_caller_name(..., 'Warn', true) % warning if called from workspace\n%\n% See also: mfilename, funpath, dbstack\n\n% Parse inputs\ndefaults.Path = false;\ndefaults.Warn = false;\nparams = defaults;\nif nargin > 0\n    params = parse_params(varargin, defaults);\nend\n\n% Get function call stack\nif params.Path\n    [ST, I] = dbstack('-completenames');\nelse\n    [ST, I] = dbstack();\nend\n\n% This function or the function that called it were called directly from\n% the workspace\nif numel(ST) < I + 2\n    caller = '';\n    if params.Warn\n        if numel(ST) < I + 1\n            warning('%s called directly from workspace.', mfilename)\n        else\n            warning('%s called directly from workspace.', ST(I + 1).name)\n        end\n    end\n    return\nend\n\n% Return caller\nif params.Path\n    caller = ST(I + 2).file;\nelse\n    caller = ST(I + 2).name;\nend\n\n\nend\n"
  },
  {
    "path": "leap/toolbox/inputParsing/nameval2struct.m",
    "content": "function S = nameval2struct(C)\n%NAMEVAL2STRUCT Converts a cell array of name-value pairs to a struct.\n% Usage:\n%   struct = nameval2struct(nameval_cell)\n%\n% See also: struct2nameval\n\nC = C(:);\n\n% Check input\nvalidateattributes(C, {'cell'}, {'vector', 'nonempty'})\nassert(iseven(length(C)), ...\n    'Cell arrays of name-value pairs must have an even number of elements.')\nassert(all(cellfun(@ischar, C(1:2:end))), ...\n    ['Cell arrays of name-value pairs must be in the format: ' ...\n    '{''Name1'', Val1, ..., ''NameN'', ValN}'])\n\n% Fix non-array inputs\nN = cellfun(@numel, C(2:2:end));\nif numel(unique(N)) ~= 1\n    C(2:2:end) = cf(@(x) {x}, C(2:2:end));\nend\n\n% Convert to structure\nS = struct(C{:});\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/inputParsing/parse_params.m",
    "content": "function [results, unmatched] = parse_params(args, defaults, varargin)\n%PARSE_PARAMS Parses a set of name-value pairs.\n% Usage:\n%   parsed = parse_params(args, defaults)\n%   parsed = parse_params(args, defaults, 'Name', Val, ...)\n%   [parsed, unmatched] = parse_params(...)\n%\n% Args:\n%   args: a cell array or structure of name-value pairs\n%   defaults: a cell array or structure of name-value pairs\n%\n% Params:\n%   This function takes any public properties of inputParser as parameters,\n%   e.g., 'StructExpand'.\n%\n% Returns:\n%   results: a structure containing the parsed results\n%   unmatched: a structure containing any args that did not have a default\n%\n% See also: inputParser, struct2nameval, nameval2struct\n\n% Make sure args are name-val cells\nif isstruct(args); args = struct2nameval(args); end\n\n% Make sure defaults are structs\nif iscell(defaults); defaults = nameval2struct(defaults); end\n\n% Default inputParser parameters\ncaller = get_caller_name();\nparser_defaults.CaseSensitive = false;\nparser_defaults.FunctionName = caller;\nparser_defaults.KeepUnmatched = true;\nparser_defaults.PartialMatching = true;\nparser_defaults.StructExpand = true;\n\n% Parse inputParser parameters\np = inputParser();\np.FunctionName = mfilename;\np = add_params(p, parser_defaults);\np.parse(varargin{:});\nparser_params = p.Results;\n\n% Create new inputParser instance\np = inputParser;\n\n% Set inputParser properties from parsed parameters\nparam_names = fieldnames(parser_params);\nfor i = 1:numel(param_names)\n    p.(param_names{i}) = parser_params.(param_names{i});\nend\n\n% Add parameters\np = add_params(p, defaults);\n\n% Parse\np.parse(args{:});\n\n% Return results of parsing\nresults = p.Results;\nunmatched = p.Unmatched;\n\nend\n\nfunction p = add_params(p, defaults)\n\n% Add defaults as parameters\nnames = fieldnames(defaults);\nfor i = 1:numel(names)\n    p.addParameter(names{i}, defaults.(names{i}));\nend\n\nend\n"
  },
  {
    "path": "leap/toolbox/inputParsing/struct2nameval.m",
    "content": "function C = struct2nameval(S)\n%STRUCT2NAMEVAL Converts a structure to a cell array of name-value pairs.\n% Usage:\n%   nameval_cell = struct2nameval(struct)\n%\n% See also: nameval2struct\n\n% Check input\nvalidateattributes(S, {'struct'}, {'nonempty', 'scalar'})\n\n% Convert to cell array\nnames = fieldnames(S);\nC = cell(1, numel(names) * 2);\nC(1:2:end) = names;\nC(2:2:end) = struct2cell(S);\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/io/GetFullPath.m",
    "content": "function File = GetFullPath(File, Style)\n% GetFullPath - Get absolute canonical path of a file or folder\n% Absolute path names are safer than relative paths, when e.g. a GUI or TIMER\n% callback changes the current directory. Only canonical paths without \".\" and\n% \"..\" can be recognized uniquely.\n% Long path names (>259 characters) require a magic initial key \"\\\\?\\\" to be\n% handled by Windows API functions, e.g. for Matlab's FOPEN, DIR and EXIST.\n%\n% FullName = GetFullPath(Name, Style)\n% INPUT:\n%   Name:  String or cell string, absolute or relative name of a file or\n%          folder. The path need not exist. Unicode strings, UNC paths and long\n%          names are supported.\n%   Style: Style of the output as string, optional, default: 'auto'.\n%          'auto': Add '\\\\?\\' or '\\\\?\\UNC\\' for long names on demand.\n%          'lean': Magic string is not added.\n%          'fat':  Magic string is added for short names also.\n%          The Style is ignored when not running under Windows.\n%\n% OUTPUT:\n%   FullName: Absolute canonical path name as string or cell string.\n%          For empty strings the current directory is replied.\n%          '\\\\?\\' or '\\\\?\\UNC' is added on demand.\n%\n% NOTE: The M- and the MEX-version create the same results, the faster MEX\n%   function works under Windows only.\n%   Some functions of the Windows-API still do not support long file names.\n%   E.g. the Recycler and the Windows Explorer fail even with the magic '\\\\?\\'\n%   prefix. Some functions of Matlab accept 260 characters (value of MAX_PATH),\n%   some at 259 already. Don't blame me.\n%   The 'fat' style is useful e.g. when Matlab's DIR command is called for a\n%   folder with les than 260 characters, but together with the file name this\n%   limit is exceeded. Then \"dir(GetFullPath([folder, '\\*.*], 'fat'))\" helps.\n%\n% EXAMPLES:\n%   cd(tempdir);                    % Assumed as 'C:\\Temp' here\n%   GetFullPath('File.Ext')         % 'C:\\Temp\\File.Ext'\n%   GetFullPath('..\\File.Ext')      % 'C:\\File.Ext'\n%   GetFullPath('..\\..\\File.Ext')   % 'C:\\File.Ext'\n%   GetFullPath('.\\File.Ext')       % 'C:\\Temp\\File.Ext'\n%   GetFullPath('*.txt')            % 'C:\\Temp\\*.txt'\n%   GetFullPath('..')               % 'C:\\'\n%   GetFullPath('..\\..\\..')         % 'C:\\'\n%   GetFullPath('Folder\\')          % 'C:\\Temp\\Folder\\'\n%   GetFullPath('D:\\A\\..\\B')        % 'D:\\B'\n%   GetFullPath('\\\\Server\\Folder\\Sub\\..\\File.ext')\n%                                   % '\\\\Server\\Folder\\File.ext'\n%   GetFullPath({'..', 'new'})      % {'C:\\', 'C:\\Temp\\new'}\n%   GetFullPath('.', 'fat')         % '\\\\?\\C:\\Temp\\File.Ext'\n%\n% COMPILE:\n%   Automatic: InstallMex GetFullPath.c uTest_GetFullPath\n%   Manual:    mex -O GetFullPath.c\n%   Download:  http://www.n-simon.de/mex\n% Run the unit-test uTest_GetFullPath after compiling.\n%\n% Tested: Matlab 6.5, 7.7, 7.8, 7.13, WinXP/32, Win7/64\n%         Compiler: LCC2.4/3.8, BCC5.5, OWC1.8, MSVC2008/2010\n% Assumed Compatibility: higher Matlab versions\n% Author: Jan Simon, Heidelberg, (C) 2009-2013 matlab.THISYEAR(a)nMINUSsimon.de\n%\n% See also: CD, FULLFILE, FILEPARTS.\n\n% $JRev: R-G V:032 Sum:7Xd/JS0+yfax Date:15-Jan-2013 01:06:12 $\n% $License: BSD (use/copy/change/redistribute on own risk, mention the author) $\n% $UnitTest: uTest_GetFullPath $\n% $File: Tools\\GLFile\\GetFullPath.m $\n% History:\n% 001: 20-Apr-2010 22:28, Successor of Rel2AbsPath.\n% 010: 27-Jul-2008 21:59, Consider leading separator in M-version also.\n% 011: 24-Jan-2011 12:11, Cell strings, '~File' under linux.\n%      Check of input types in the M-version.\n% 015: 31-Mar-2011 10:48, BUGFIX: Accept [] as input as in the Mex version.\n%      Thanks to Jiro Doke, who found this bug by running the test function for\n%      the M-version.\n% 020: 18-Oct-2011 00:57, BUGFIX: Linux version created bad results.\n%      Thanks to Daniel.\n% 024: 10-Dec-2011 14:00, Care for long names under Windows in M-version.\n%      Improved the unittest function for Linux. Thanks to Paul Sexton.\n% 025: 09-Aug-2012 14:00, In MEX: Paths starting with \"\\\\\" can be non-UNC.\n%      The former version treated \"\\\\?\\C:\\<longpath>\\file\" as UNC path and\n%      replied \"\\\\?\\UNC\\?\\C:\\<longpath>\\file\".\n% 032: 12-Jan-2013 21:16, 'auto', 'lean' and 'fat' style.\n\n% Initialize: ==================================================================\n% Do the work: =================================================================\n\n% #############################################\n% ### USE THE MUCH FASTER MEX ON WINDOWS!!! ###\n% #############################################\n\n% Difference between M- and Mex-version:\n% - Mex does not work under MacOS/Unix.\n% - Mex calls Windows API function GetFullPath.\n% - Mex is much faster.\n\n% Magix prefix for long Windows names:\nif nargin < 2\n   Style = 'auto';\nend\n\n% Handle cell strings:\n% NOTE: It is faster to create a function @cell\\GetFullPath.m under Linux, but\n% under Windows this would shadow the fast C-Mex.\nif isa(File, 'cell')\n   for iC = 1:numel(File)\n      File{iC} = GetFullPath(File{iC}, Style);\n   end\n   return;\nend\n\n% Check this once only:\nisWIN    = strncmpi(computer, 'PC', 2);\nMAX_PATH = 260;\n\n% Warn once per session (disable this under Linux/MacOS):\npersistent hasDataRead\nif isempty(hasDataRead)\n   % Test this once only - there is no relation to the existence of DATAREAD!\n   %if isWIN\n   %   Show a warning, if the slower Matlab version is used - commented, because\n   %   this is not a problem and it might be even useful when the MEX-folder is\n   %   not inlcuded in the path yet.\n   %   warning('JSimon:GetFullPath:NoMex', ...\n   %      ['GetFullPath: Using slow Matlab-version instead of fast Mex.', ...\n   %       char(10), 'Compile: InstallMex GetFullPath.c']);\n   %end\n   \n   % DATAREAD is deprecated in 2011b, but still available. In Matlab 6.5, REGEXP\n   % does not know the 'split' command, therefore DATAREAD is preferred:\n   hasDataRead = ~isempty(which('dataread'));\nend\n\nif isempty(File)  % Accept empty matrix as input:\n   if ischar(File) || isnumeric(File)\n      File = cd;\n      return;\n   else\n      error(['JSimon:', mfilename, ':BadTypeInput1'], ...\n         ['*** ', mfilename, ': Input must be a string or cell string']);\n   end\nend\n\nif ischar(File) == 0  % Non-empty inputs must be strings\n   error(['JSimon:', mfilename, ':BadTypeInput1'], ...\n      ['*** ', mfilename, ': Input must be a string or cell string']);\nend\n\nif isWIN  % Windows: --------------------------------------------------------\n   FSep = '\\';\n   File = strrep(File, '/', FSep);\n   \n   % Remove the magic key on demand, it is appended finally again:\n   if strncmp(File, '\\\\?\\', 4)\n      if strncmpi(File, '\\\\?\\UNC\\', 8)\n         File = ['\\', File(7:length(File))];  % Two leading backslashes!\n      else\n         File = File(5:length(File));\n      end\n   end\n   \n   isUNC   = strncmp(File, '\\\\', 2);\n   FileLen = length(File);\n   if isUNC == 0                        % File is not a UNC path\n      % Leading file separator means relative to current drive or base folder:\n      ThePath = cd;\n      if File(1) == FSep\n         if strncmp(ThePath, '\\\\', 2)   % Current directory is a UNC path\n            sepInd  = strfind(ThePath, '\\');\n            ThePath = ThePath(1:sepInd(4));\n         else\n            ThePath = ThePath(1:3);     % Drive letter only\n         end\n      end\n      \n      if FileLen < 2 || File(2) ~= ':'  % Does not start with drive letter\n         if ThePath(length(ThePath)) ~= FSep\n            if File(1) ~= FSep\n               File = [ThePath, FSep, File];\n            else                        % File starts with separator:\n               File = [ThePath, File];\n            end\n         else                           % Current path ends with separator:\n            if File(1) ~= FSep\n               File = [ThePath, File];\n            else                        % File starts with separator:\n               ThePath(length(ThePath)) = [];\n               File = [ThePath, File];\n            end\n         end\n         \n      elseif FileLen == 2 && File(2) == ':'   % \"C:\" current directory on C!\n         % \"C:\" is the current directory on the C-disk, even if the current\n         % directory is on another disk! This was ignored in Matlab 6.5, but\n         % modern versions considers this strange behaviour.\n         if strncmpi(ThePath, File, 2)\n            File = ThePath;\n         else\n            try\n               File = cd(cd(File));\n            catch    % No MException to support Matlab6.5...\n               if exist(File, 'dir')  % No idea what could cause an error then!\n                  rethrow(lasterror);\n               else  % Reply \"K:\\\" for not existing disk:\n                  File = [File, FSep];\n               end\n            end\n         end\n      end\n   end\n   \nelse         % Linux, MacOS: ---------------------------------------------------\n   FSep = '/';\n   File = strrep(File, '\\', FSep);\n   \n   if strcmp(File, '~') || strncmp(File, '~/', 2)  % Home directory:\n      HomeDir = getenv('HOME');\n      if ~isempty(HomeDir)\n         File(1) = [];\n         File    = [HomeDir, File];\n      end\n      \n   elseif strncmpi(File, FSep, 1) == 0\n      % Append relative path to current folder:\n      ThePath = cd;\n      if ThePath(length(ThePath)) == FSep\n         File = [ThePath, File];\n      else\n         File = [ThePath, FSep, File];\n      end\n   end\nend\n\n% Care for \"\\.\" and \"\\..\" - no efficient algorithm, but the fast Mex is\n% recommended at all!\nif ~isempty(strfind(File, [FSep, '.']))\n   if isWIN\n      if strncmp(File, '\\\\', 2)  % UNC path\n         index = strfind(File, '\\');\n         if length(index) < 4    % UNC path without separator after the folder:\n            return;\n         end\n         Drive            = File(1:index(4));\n         File(1:index(4)) = [];\n      else\n         Drive     = File(1:3);\n         File(1:3) = [];\n      end\n   else  % Unix, MacOS:\n      isUNC   = false;\n      Drive   = FSep;\n      File(1) = [];\n   end\n   \n   hasTrailFSep = (File(length(File)) == FSep);\n   if hasTrailFSep\n      File(length(File)) = [];\n   end\n   \n   if hasDataRead\n      if isWIN  % Need \"\\\\\" as separator:\n         C = dataread('string', File, '%s', 'delimiter', '\\\\');  %#ok<REMFF1>\n      else\n         C = dataread('string', File, '%s', 'delimiter', FSep);  %#ok<REMFF1>\n      end\n   else  % Use the slower REGEXP, when DATAREAD is not available anymore:\n      C = regexp(File, FSep, 'split');\n   end\n   \n   % Remove '\\.\\' directly without side effects:\n   C(strcmp(C, '.')) = [];\n   \n   % Remove '\\..' with the parent recursively:\n   R = 1:length(C);\n   for dd = reshape(find(strcmp(C, '..')), 1, [])\n      index    = find(R == dd);\n      R(index) = [];\n      if index > 1\n         R(index - 1) = [];\n      end\n   end\n   \n   if isempty(R)\n      File = Drive;\n      if isUNC && ~hasTrailFSep\n         File(length(File)) = [];\n      end\n      \n   elseif isWIN\n      % If you have CStr2String, use the faster:\n      %   File = CStr2String(C(R), FSep, hasTrailFSep);\n      File = sprintf('%s\\\\', C{R});\n      if hasTrailFSep\n         File = [Drive, File];\n      else\n         File = [Drive, File(1:length(File) - 1)];\n      end\n      \n   else  % Unix:\n      File = [Drive, sprintf('%s/', C{R})];\n      if ~hasTrailFSep\n         File(length(File)) = [];\n      end\n   end\nend\n\n% \"Very\" long names under Windows:\nif isWIN\n   if ~ischar(Style)\n      error(['JSimon:', mfilename, ':BadTypeInput2'], ...\n         ['*** ', mfilename, ': Input must be a string or cell string']);\n   end\n   \n   if (strncmpi(Style, 'a', 1) && length(File) >= MAX_PATH) || ...\n         strncmpi(Style, 'f', 1)\n      % Do not use [isUNC] here, because this concerns the input, which can\n      % '.\\File', while the current directory is an UNC path.\n      if strncmp(File, '\\\\', 2)  % UNC path\n         File = ['\\\\?\\UNC', File(2:end)];\n      else\n         File = ['\\\\?\\', File];\n      end\n   end\nend\n\n% return;\n"
  },
  {
    "path": "leap/toolbox/io/dir_ext.m",
    "content": "function matches = dir_ext(path, extensions, return_paths)\n%DIR_EXT Returns files in a directory with the matching extension.\n% Usage:\n%   matches = dir_ext(path, extension)\n%   matches = dir_ext(path, extensions) % cell array of strings\n%   matches = dir_ext(path, _, true) % returns full paths\n%\n% Note: The leading '.' can be omitted.\n%\n% See also: dir_regex, dir_paths\n\nif nargin < 2\n    extensions = path;\n    path = pwd;\nend\n\n% Process arguments\nif ischar(extensions); extensions = {extensions}; end\nif ~iscellstr(extensions)\n    error('Extension must be a string or cell array of strings.')\nend\nif nargin < 3; return_paths = false; end\n\n% Remove leading '.'\nextensions = regexprep(extensions, '^\\.', '');\n\n% Build regex pattern\npattern = ['\\.(' strjoin(extensions, '|') ')$'];\n\n% Test for the extensions\nmatches = dir_regex(path, pattern, return_paths);\n\nend\n"
  },
  {
    "path": "leap/toolbox/io/dir_paths.m",
    "content": "function [paths, base_path] = dir_paths(path, type)\n%DIR_PATHS Returns the full paths of a directory listing.\n% Usage:\n%   paths = dir_paths % lists paths in pwd\n%   paths = dir_paths(path)\n%   paths = dir_paths(path, 'files') % 'files', 'folders' or 'both' (default)\n%   [paths, base_path] = dir_paths(...)\n%\n% See also: dir, dir_files, dir_folders\n\n% Process arguments\nif nargin < 1; path = pwd; end\nif nargin < 2; type = 'both'; end\ntype = validatestring(type, {'folders', 'files', 'both', 'all'});\n\n% Get directory listing\nlisting = dir(path);\nnames = {listing.name}';\n\n% Filter relative names ('.' and '..')\nrel_names = cellfun(@(x) isequal(x, '.') || isequal(x, '..'), {listing.name});\n\n% Filter files or folders\nfolders = [listing.isdir];\nfiles = ~[listing.isdir];\nswitch type\n    case 'folders'\n        names = names(folders & ~rel_names);\n    case 'files'\n        names = names(files & ~rel_names);\n    otherwise\n        names = names((folders | files) & ~rel_names);\nend\n\n% Account for wilcard usage or direct filenames\nif instr('*', path) || isfile(path)\n    base_path = GetFullPath(fileparts(path)); % get parent directory\nelse\n    base_path = GetFullPath(path);\nend\nbase_path = regexprep(base_path, '[\\\\/]*$', ''); % remove any trailing slash\n\n% Append base path\npaths = fullfile(base_path, names);\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/io/dir_regex.m",
    "content": "function matches = dir_regex(path, expression, return_paths)\n%DIR_REGEX Returns contents in the path matching a regular expression.\n% Usage:\n%   matches = dir_regex(path, expression)\n%   matches = dir_regex(path, expression, true) % returns full paths\n%\n% See also: regexp, dir_ext, dir_paths\n\n% Default: returns just filenames\nif nargin < 3; return_paths = false; end\n\n% Get directory listing\nlisting = dir_paths(path);\n\n% Test regex\nmatches = listing(~areempty(regexp(listing, expression)));\n\n% Singleton output\nif iscell(matches) && numel(matches) == 1\n    matches = matches{1};\nend\n\n% Return just the filenames\nif ~return_paths\n    matches = get_filename(matches);\nend\n\nend\n"
  },
  {
    "path": "leap/toolbox/io/exists.m",
    "content": "function TF = exists(path, dir_only)\n%EXISTS Returns true if the specified path exists in the filesystem.\n% Usage:\n%   TF = exists(path)\n%   TF = exists(paths)\n%   TF = exists(_, dir_only)\n%\n% Args:\n%   path: string path\n%   paths: cell array of paths or scalar structure where fields are paths\n%   dir_only: if true, returns true only if the path is a folder (default: false)\n%\n% Notes:\n%   - This function differs from exist() in that it returns true only if\n%     the path exists in the filesystem.\n%     The exist function may return 2 if the path is a file and a function\n%     exists in the MATLAB search path but the file does not exist.\n%   - Existence is checked using the dir() function.\n%\n% See also: exist, isfile, isfolder\n\nnarginchk(1, 2)\nvalidateattributes(path, {'char', 'cell', 'struct'}, {'nonempty'})\n\nif nargin < 2\n    dir_only = false;\nend\n\n% Single path specified\nif ischar(path)\n    path = {path};\nend\n\n% Structure of paths specified\nstruct_output = false;\nif isstruct(path)\n    fields = fieldnames(path);\n    path = struct2cell(path);\n    struct_output = true;\nend\n\n% Convert [] to ''\npath(areempty(path)) = {''};\n\n% Check input\nif ~iscellstr(path)\n    error('Expected input to be string, cell array of strings or structure of paths.')\nend\n\n% Check if paths exist\nTF = cellfun(@(p) ~isempty(dir(p)), path);\n\n% Only paths to directories\nif dir_only\n    TF = TF & isfolder(path);\nend\n\n% Return structure\nif struct_output\n    TF = cell2struct(num2cell(TF), fields);\nend\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/io/ext2filter_spec.m",
    "content": "function filter_spec = ext2filter_spec(exts)\n%EXT2FILTER_SPEC Generates a filter specification from a list of file extensions.\n% Usage:\n%   filter_spec = ext2filter_spec(ext) % single extension\n%   filter_spec = ext2filter_spec(exts)\n%\n% See also: uigetfile, uigetdir, uibrowse\n\nif ischar(exts)\n    exts = {exts};\nend\n\nif ~iscellstr(exts)\n    error('Expected a string or cell array of strings.')\nend\n\n% Keep only characters afte the period\nexts = regexprep(exts, '.*\\.', '');\n\n% Create filter specification string\nfilter_spec = ['*.' strjoin(exts, ';*.')];\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/io/extrep.m",
    "content": "function new_path = extrep(filepath, new_ext)\n%EXTREP Replace the extension of a file path.\n% Usage:\n%   newpath = extrep(filepath, old_ext, new_ext)\n% \n% Args:\n%   filepath: a path to a file with an extension\n%   new_ext: new file extension\n%\n% Returns:\n%   new_path: path with extension replaced\n% \n% See also: get_ext, dir_ext\n\nif new_ext(1) == '.'; new_ext = new_ext(2:end); end\n\npattern = '(?<=\\.)[^\\\\/.]+$';\nnew_path = regexprep(filepath, pattern, new_ext);\n\nend\n"
  },
  {
    "path": "leap/toolbox/io/funpath.m",
    "content": "function path = funpath(~)\n%FUNPATH Returns the path to the calling function.\n% Usage:\n%   path = funpath()\n%   path = funpath(_) % returns the path to the parent directory\n%\n% See also: get_caller_name, which, dbstack\n\n% Get the path to the calling function\npath = get_caller_name('path', true);\n\n% Return the path to the function's parent directory\nif nargin > 0\n    path = fileparts(path);\nend\nend\n\n"
  },
  {
    "path": "leap/toolbox/io/get_ext.m",
    "content": "function ext = get_ext(path, no_dot)\n%GET_EXT Returns the extension in a path. The path need not exist.\n% Usage:\n%   ext = get_ext(path)\n%   ext = get_ext(paths) % cell array of paths\n%   ext = get_ext(_, no_dot) % if true, returns ext without dot (default: false)\n%\n% Notes:\n%   - Extension returned will be the characters after the LAST dot.\n%   - Returns '' if path has no extension.\n%\n% See also: get_filename, fileparts\n\n% Default\nif nargin < 2\n    no_dot = false;\nend\n\n% Regular expression\npattern = '\\.[^\\\\/\\.]+$';\nif no_dot\n    pattern = '(?<=\\.)[^\\\\/.]+$';\nend\n\n% Get matches\next = regexp(path, pattern, 'match', 'once');\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/io/get_filename.m",
    "content": "function filename = get_filename(path, no_ext)\n%GET_FILENAME Returns the filename in a path. The path need not exist.\n% Usage:\n%   filename = get_filename(path)\n%   filename = get_filename(paths) % cell array of paths\n%   filename = get_filename(_, true) % no extension\n%\n% See also: get_ext, fileparts\n\nif nargin < 2\n    no_ext = false;\nend\n\nif ischar(path)\n    [~, filename, ext] = fileparts(path);\n    if ~no_ext\n        filename = [filename ext];\n    end\nelseif iscellstr(path)\n    filename = cellfun(@(p) get_filename(p, no_ext), path, 'UniformOutput', false);\nend\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/io/get_filesize.m",
    "content": "function bytes = get_filesize(file_path)\n%GET_FILESIZE Returns the size of the specified file in bytes.\n% This is a wrapper for dir().\n%\n% Usage:\n%   bytes = get_filesize(file_path)\n%\n% See also: dir\n\n% Get file attributes\nattributes = dir(file_path);\nbytes = attributes.bytes;\n\nif nargout == 0\n    printf('%s: *%s*', get_filename(file_path), bytes2str(bytes))\n    clear bytes\nend\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/io/get_new_filename.m",
    "content": "function new_filename = get_new_filename(filename, noSpaces)\n%GET_NEW_FILENAME Returns a new filename based on the one specified.\n% Usage:\n%   new_filename = get_new_filename(filename)\n%   new_filename = get_new_filename(filename, true) % no spaces\n\nif nargin < 2 || isempty(noSpaces); noSpaces = false; end\n\n% Break filename into components\n[pathstr, name, ext] = fileparts(filename);\nbase = fullfile(pathstr, name);\n\n% Iterate until we find a non-existing filename\nnum = 1;\nnew_filename = [base ext];\nwhile exists(new_filename)\n    num = num + 1;\n    if noSpaces\n        new_filename = [base '_' num2str(num) ext];\n    else\n        new_filename = [base ' (' num2str(num) ')' ext];\n    end\nend\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/io/lastdir.m",
    "content": "function dir_path = lastdir(new_path)\n%LASTDIR Remembers the last directory used for use in UI browsing dialogs.\n% Usage:\n%   dir_path = lastdir\n%   lastuidir(new_path)\n%\n% Args:\n%   new_path: relative or absolute path to a folder or file.\n%       Saves parent folder if path to a file is specified.\n%\n% Returns:\n%   dir_path: absolute path to the last used directory (defaults to current\n%       directory)\n\npersistent last_dir;\ncache_file = fullfile(fileparts(funpath()), '.lastdir');\n\n%% Update last directory\nif nargin > 0\n    % Get absolute path (pwd if invalid path)\n    new_path = GetFullPath(new_path);\n    \n    % Get folder from path\n    if ~isdir(new_path)\n        new_path = fileparts(new_path);\n    end\n    \n    % Check if it exists\n    if ~exists(new_path)\n        error('Specified path does not exist.')\n    end\n    \n    % Check if we changed anything before updating\n    path_updated = isequal(last_dir, new_path);\n    \n    % Update to new path\n    last_dir = new_path;\n    \n    % Write to cache\n    if path_updated || ~exists(cache_file)\n        try\n            f = fopen(cache_file, 'w');\n            fprintf(f, '%s', last_dir);\n            fclose(f);\n        catch\n        end\n    end\n    return\nend\n\n%% Get the last directory\nif isempty(last_dir) || ~ischar(last_dir) || ~exists(last_dir)\n    % Reset to current folder by default\n    last_dir = pwd;\n    \n    % Use last directory from cache file if it exists\n    if exists(cache_file)\n        cache = fileread(cache_file);\n        \n        if exists(cache)\n            last_dir = cache;\n        end\n    end\nend\n\n%% Return last directory\nif nargout > 0\n    dir_path = last_dir;\nend\n\n%% Output last directory to console\nif nargout == 0\n    fprintf('Last saved directory: %s\\n', last_dir)\nend\nend\n\n"
  },
  {
    "path": "leap/toolbox/io/mkdirto.m",
    "content": "function TF = mkdirto(path)\n%MKDIRTO Quietly makes all directories to path that do not currently exist.\n% Usage:\n%   mkdirto(path)\n%   TF = mkdirto(path) % returns true if a folder was created\n%\n% See also: mkdir\n\nwarning('off','MATLAB:MKDIR:DirectoryExists')\nif ~isempty(get_ext(path)); path = fileparts(path); end\nTF = mkdir(path);\nwarning('on','MATLAB:MKDIR:DirectoryExists')\nif nargout < 1; clear TF; end\nend\n\n"
  },
  {
    "path": "leap/toolbox/io/uibrowse.m",
    "content": "function [path, filter_idx]  = uibrowse(filter_spec, start_path, dialog_title, type)\n%UIBROWSE Displays a file or folder selection dialog.\n% Usage:\n%   path = uibrowse\n%   path = uibrowse(filter_spec)\n%   path = uibrowse(filter_spec, start_path)\n%   path = uibrowse(filter_spec, start_path, dialog_title)\n%   path = uibrowse(filter_spec, start_path, dialog_title, type)\n%   [path, filter_idx] = uibrowse(...)\n%\n% Args:\n%   filter_spec: file filter specification (default = '')\n%   start_path: starting path, optionally including filename (default = last\n%       used directory)\n%   dialog_title: dialog window title (default = 'Select file...')\n%   type: 'file': select existing file (default)\n%         'savefile': choose filename and location for saving\n%         'dir': select folder\n%\n% Returns:\n%   path: absolute path to selection\n%   filter_idx: index of the filter specification chosen\n%\n% Notes:\n%   - This function is a wrapper for uigetfile, uiputfile and uigetdir.\n%     See the help for those functions for help on filter_spec format, or\n%     use ext2filter_spec().\n%   - Last directory is remembered between calls to this function.\n%   - Error is thrown if the user cancels or does not select a file.\n%\n% See also: ext2filter_spec, uigetvideo, uigetfile, uigetdir, lastdir\n\n% Default filter specification\nif nargin < 1 || isempty(filter_spec)\n    filter_spec = '';\nend\n\n% Get last directory\nif nargin < 2 || isempty(start_path) || ~exists(start_path)\n    start_path = lastdir();\nend\n\n% Defaults\nif nargin < 3; dialog_title = []; end\nif nargin < 4; type = 'file'; end\ntype = validatestring(type, {'file', 'savefile', 'dir'});\n\n% Display browse dialog based on type\nswitch type\n    case 'file'\n        if isempty(dialog_title); dialog_title = 'Select file...'; end\n        [filename, dir_path, filter_idx] = uigetfile(filter_spec, dialog_title, start_path);\n    case 'savefile'\n        if isempty(dialog_title); dialog_title = 'Select save location...'; end\n        [filename, dir_path, filter_idx] = uiputfile(filter_spec, dialog_title, start_path);\n    case 'dir'\n        if isempty(dialog_title); dialog_title = 'Select folder...'; end\n        dir_path = uigetdir(start_path, dialog_title);\n        filename = '';\n        filter_idx = [];\nend\n\n% Check if user hit Cancel, closed the dialog or didn't select a file\nif dir_path == 0\n    error('No file or folder selected.')\nend\n\n% Save last dir\nlastdir(dir_path);\n\n% Return full path to selection\npath = fullfile(dir_path, filename);\nend\n\n"
  },
  {
    "path": "leap/toolbox/ml/ezpca.m",
    "content": "function pcs = ezpca(X, varargin)\n%EZPCA PCA -- quick and easy!\n% Usage:\n%   pcs = ezpca(X)\n% \n% Args:\n%   X: N x D data\n%\n% Notes:\n%       Project: (X - pcs.mu) * pcs.coeff\n%   Reconstruct: (pcs.score * pcs.coeff') + pcs.mu\n% \n% See also: pca\n\n[coeff, score, latent, tsquared, explained, mu] = pca(X, varargin{:});\n\nif nargout < 1\n    figure, figclosekey\n    subplot(1,2,1)\n    imgsc(coeff) % columns = pcs\n    xlabel('PCs'), ylabel('Dimensions')\n    \n    subplot(1,2,2)\n    ax = plotExplainedVar(explained);\n    hold(ax(2),'on')\n    \n    hline(ax(2), 95, 'r-')\n    hline(ax(2), 99.5, 'g-')\n    \n    cum_explained = cumsum(explained);\n    i95 = find(cum_explained >= 95, 1);\n    i995 = find(cum_explained >= 99.5, 1);\n    \n    plot(ax(2), i95, cum_explained(i95), 'r.', 'MarkerSize',15)\n    plot(ax(2), i995, cum_explained(i995), 'g.', 'MarkerSize',15)\n    \n    figsize(1200,400)\n    \n    return\nend\n\npcs = varstruct(coeff, score, latent, tsquared, explained, mu);\n\n\nend\n"
  },
  {
    "path": "leap/toolbox/strings/bytes2str.m",
    "content": "function [str, x_bytes, unit] = bytes2str(bytes, precision)\n%BYTES2STR Returns the number of bytes in a more readable format.\n% Usage:\n%   bytes2str(bytes)\n%   str = bytes2str(bytes)\n%   str = bytes2str(filename)\n%   str = bytes2str(_, precision)\n%   [str, x_bytes, unit] = bytes2str(...)\n\nif ischar(bytes)\n    bytes = get_filesize(bytes);\nend\nif nargin < 2\n    precision = 3;\nend\n\nunits = {'bytes', 'KB', 'MB', 'GB', 'TB', 'PB'};\n\n% Find closest units\nbase = floor(log(bytes) / log(1024));\n\n% Convert to new units\nx_bytes = bytes * (1024 ^ -base);\n\n% Convert to string\nunit = units{base + 1};\nstr = sprintf('%s %s', num2str(x_bytes, precision), unit);\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/strings/instr.m",
    "content": "function TF = instr(needle, haystack, flags)\n%INSTR Returns true if (any) needle is in (any) haystack.\n% Usage:\n%   TF = instr(needle, haystack)\n%   TF = instr(needle, haystack, flags)\n%\n% Args:\n%   needle: string or cell array of strings to look for.\n%   haystack: the string or cell array of strings to look in.\n%   flags: indicates the matching mode:\n%       's' => True if needle is a substring of haystack (default)\n%       'r' => True if regexp(needle, haystack) returns a match\n%       'e' => Looks for exact match (may be case-insensitive)\n%\n%       You can combine these with the modifier flags:\n%       'c' => Case-sensitive\n%       'a' => Evaluate ALL needles (strcmp behavior)\n%       Default: 's'\n%\n% Returns:\n%   TF logical indicating if needle is in haystack.\n%       If either the needle or the haystack are cell arrays and the 'a'\n%       flag is not set (default), this function returns a scalar\n%       indicating whether ANY needle was found in ANY haystack.\n%\n% See also: strcmp, strfind, validatestr, regexp\n\n% Parse input\nnarginchk(2, 3)\nif nargin < 3; flags = 's'; else flags = lower(flags); end\nvalid_flags = 'rseca';\np = inputParser;\np.addRequired('needle', @(x) ischar(x) || iscellstr(x));\np.addRequired('haystack', @(x) ischar(x) || iscellstr(x));\np.addOptional('flags', 's', @(x) ischar(x) && all(arrayfun(@(flagchar) any(valid_flags == flagchar), x)));\np.parse(needle, haystack, flags);\nneedle = p.Results.needle;\nhaystack = p.Results.haystack;\nflags = p.Results.flags;\n\n% Matching mode flags\nregex = any(flags == 'r');\nexact = any(flags == 'e');\nsubstr = any(flags == 's') || (~regex && ~exact);\n\n% Modifier flags\ncase_sensitive = any(flags == 'c');\nreturn_all = any(flags == 'a');\n\n% Make sure inputs are cell strings\nif ~iscellstr(needle); needle = {needle}; end\nif ~iscellstr(haystack); haystack = {haystack}; end\n\n% Figure out matching function\nif substr\n    if case_sensitive\n        % strfind(str, pattern): searches str for occurrences of pattern\n        f = @(pattern, str) any(cellfun(@(s) ~isempty(strfind(s, pattern)), str));\n    else\n        f = @(pattern, str) any(cellfun(@(s) ~isempty(strfind(lower(s), lower(pattern))), str));\n    end\nelseif regex\n    % regexp(str, expressions): tests str with expressions\n    case_option = 'ignorecase'; if case_sensitive; case_option = 'matchcase'; end\n    f = @(str, expressions) any(~cellfun('isempty', regexp(str, expressions, case_option)));\nelseif exact\n    if case_sensitive\n        f = @(needle, haystacks) any(strcmp(needle, haystacks));\n    else\n        f = @(needle, haystacks) any(strcmpi(needle, haystacks));\n    end\nend\n\n% Match\nTF = false(size(needle));\nfor i = 1:numel(needle)\n    % Returns true if needle i is in ANY haystack\n    found = f(needle{i}, haystack);\n    \n    % Save result for needle i\n    TF(i) = found;\n    \n    % Return as soon as we find needle\n    if ~return_all && found\n        TF = true;\n        return\n    end\nend\n\nif ~return_all\n    TF = false;\nend\nend\n\n"
  },
  {
    "path": "leap/toolbox/strings/printf.m",
    "content": "function formatted = printf(str, varargin)\n%PRINTF Prints formatted output.\n% Usage:\n%   printf(str, ...)\n%   formatted = printf(str, ...)\n%\n% Formatting:\n%   - Surround text with '*' for bold or '_' for underlining\n%   - Set flags by adding '#[flagname]' to the END of the string\n%       Flags:\n%       '#nonl': Will not print with a newline at the end of the string.\n%       '#[color]': Sets the color of the string.\n%                   Ex: '#red', '#blue', '#green', '#orange'\n%\n% Example:\n%   printf('*bold* _underlined_ #red')\n\nif nargin < 1; str = ''; end\n\n% Flag defaults\ncolor = 'black';\nappendNewline = true;\n\n% Look for tags\n[match, tokens] = regexp(str, '(\\s?#\\w+)+[\\\\n]*$', 'match', 'tokens');\n%   match: whole match, include possible newline\n%   tokens: cell with only the capturing group (the tags)\n\nif ~isempty(match)\n    flags = tokens{1}{1};\n    \n    % Remove the tags from the original string\n    str = regexprep(str, [match '$'], strrep(match, flags, ''));\n    \n    % Process flags\n    flags = regexp(flags, '#(\\w+)', 'tokens');\n    for i = 1:numel(flags)\n        flag = flags{i}{1};\n        switch flag\n            case 'nonl'\n                appendNewline = false;\n            otherwise\n                color = flag;\n        end\n    end\nend\n\n% Add bold tags\nstr = regexprep(str, '\\*(.+?)\\*', '<strong>$1</strong>');\n\n% Add underlining (empty link)\nstr = regexprep(str, '\\_(.+?)\\_', '<a href=\"\">$1</a>');\n\n% Add color\nif ~strcmpi(color, 'black')\n    str = ['<font color=\"' color '\">' str '</font>'];\nend\n\n% Append newline\nendsWithNewline = ~isempty(regexp(str, '\\\\n(</font>)?$', 'once'));\nif ~endsWithNewline && appendNewline\n    str = [str '\\n'];\nend\n\nif nargout < 1\n    % Print\n    fprintf(str, varargin{:})\nelse\n    % Format\n    formatted = sprintf(str, varargin{:});\nend\nend\n\n"
  },
  {
    "path": "leap/toolbox/strings/secs2hms.m",
    "content": "function [h, m, s] = secs2hms(numSecs)\n%SECS2HMS Converts a number of seconds to hours, minutes and fractional seconds.\n% Usage:\n%   hms = secs2hms(numSecs) % numeric vector\n%   [h, m, s] = secs2hms(numSecs)\n%\n% See also: duration, hms, secs2str\n\n[h,m,s] = hms(duration([0 0 numSecs]));\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/strings/secsf.m",
    "content": "function str = secsf(format, numSecs)\n%SECSF Yet another seconds formatting function.\n% Usage:\n%   str = secsf(format, numSecs)\n%\n% Args:\n%   format: formatted string with any of these tokens:\n%       No leading zero: %h, %m, %s\n%       Leading zero: %H, %M, %S\n%       Fractional seconds: %s.ms or %S.ms\n%\n% See also: secs2str, secs2hms, secstr\n\nif nargin < 2\n    [format, numSecs] = swap(format, '%hh %mm %ss');\nend    \n\n[h,m,s] = secs2hms(numSecs);\n\nstr = format;\n\nstr = regexprep(str, '%h', num2str(round(h),'%d'));\nstr = regexprep(str, '%H', num2str(round(h),'%02d'));\n\nstr = regexprep(str, '%m', num2str(round(m),'%d'));\nstr = regexprep(str, '%M', num2str(round(m),'%02d'));\n\nstr = regexprep(str, '%s\\.ms', num2str(s,'%f'));\nms = num2str(s-round(s),'%f'); ms = ms(2:end); % '.#####'\nstr = regexprep(str, '%S\\.ms', [num2str(s,'%02d') ms]);\n\nstr = regexprep(str, '%s', num2str(round(s),'%d'));\nstr = regexprep(str, '%S', num2str(round(s),'%02d'));\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/utilities/af.m",
    "content": "function out = af(func,varargin)\n%AF Convenience wrapper for arrayfun with non-uniform output.\n% Usage:\n%   out = af(func, A); % equivalent to out = arrayfun(func, A, 'UniformOutput, false)\n\nout = arrayfun(func, varargin{:}, 'UniformOutput', false);\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/utilities/arange.m",
    "content": "function [min_val, max_val] = arange(X)\n%ARANGE Returns the range (min and max) of an entire array.\n% Usage:\n%   R = arange(X)\n%   [min_val, max_val] = arange(X)\n%\n% See also: alims, amin, amax\n\nmin_val = min(X(:));\nmax_val = max(X(:));\n\nif nargout < 2\n    min_val = [min_val, max_val];\nend\nend\n\n"
  },
  {
    "path": "leap/toolbox/utilities/areempty.m",
    "content": "function empties = areempty(cellarr)\n%AREEMPTY Returns a logical array of the size of the cell array indicating whether each cell is empty.\n% Usage:\n%   empties = areempty(cellarr)\n%\n% See also: isempty\n\nempties = cellfun('isempty', cellarr);\n\nend"
  },
  {
    "path": "leap/toolbox/utilities/argmin.m",
    "content": "function idx = argmin(X, dim)\n%ARGMAX Returns the index at which the min is found.\n% Usage:\n%   idx = argmin(X)\n%   idx = argmin(X, dim)\n\nif isvector(X)\n    [~, idx] = min(X);\nelse\n    if nargin < 2; dim = 1; end\n    [~, idx] = min(X, [], dim);\nend\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/utilities/arr2cell.m",
    "content": "function C = arr2cell(X, dim)\n%ARR2CELL Splits an array into a cell across the specified dimension.\n% Usage:\n%   C = arr2cell(X) % default: last dimension\n%   C = arr2cell(X, dim)\n%\n% See also: num2cell, stack2cell\n\nif nargin < 2; dim = ndims(X); end\n\n% Select every other dimension\ndims = setdiff(1:ndims(X), dim);\n\n% Convert to cell and squeeze\nC = squeeze(num2cell(X,dims));\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/utilities/cell1.m",
    "content": "function varargout = cell1(sz, dim)\n%CELL1 Creates a 1d empty cell array. Convenience for cell(sz, 1).\n% Usage:\n%   C = cell1(sz)\n%   C = cell1(sz, dim)\n% \n% Args:\n%   sz: length of the cell array\n%   dim: along which dimension to create cell array (default = 1)\n%\n% Returns:\n%   C: empty cell array with sz elements in dimension dim\n% \n% See also: cell\n\nif nargin < 2\n    dim = 1;\nend\n\nif ~isscalar(sz); sz = length(sz); end\n\nsz2 = ones(1,max(2,dim));\nsz2(dim) = sz;\n\nC = cell(sz2);\n\nvarargout = {C};\nif nargout > 1\n    varargout = repmat({C},1,nargout);\nend    \n\nend\n"
  },
  {
    "path": "leap/toolbox/utilities/cellcat.m",
    "content": "function [X,idx] = cellcat(C, dim)\n%CELLCAT Unpacks and concatenates a cell array. Shorthand for: cat(dim, C{:})\n% Usage:\n%   X = cellcat(C)\n%   X = cellcat(C, dim)\n%   [X, idx] = cellcat(_)\n%\n% Args:\n%   C: cell array\n%   dim: dimension along which to concatenate (default = 1)\n%       if empty, finds first dimension along which not all sizes are the same\n%\n% Returns:\n%   X: array resulting from concatenating elements of C\n%   idx: vector of the same length as X with the corresponding indices in C\n%\n% See also: cat\n\nif nargin < 2; dim = 1; end\nif isempty(dim)\n    maxDims = max(cellfun(@ndims, C));\n    for dim = 1:maxDims\n        sz = cellfun(@(x)size(x,dim), C);\n        if nunique(sz) > 1\n            break\n        end\n    end\nend\n\nX = cat(dim, C{:});\n\nif nargout > 1\n    N = cellfun(@(x)size(x,dim),C);\n    idx = repelem(vert(1:numel(C)),N(:));\nend\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/utilities/cf.m",
    "content": "function out = cf(func,varargin)\n%CF Convenience wrapper for cellfun with non-uniform output.\n% Usage:\n%   out = cf(func, C); % equivalent to out = cellfun(func, C, 'UniformOutput, false)\n\nout = cellfun(func, varargin{:}, 'UniformOutput', false);\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/utilities/clip.m",
    "content": "function X2 = clip(X, bounds, varargin)\n%CLIP Clip values in an array to lower and upper bounds.\n% Usage:\n%   X2 = clip(X, [lo up])\n%   X2 = clip(X, [lo up], 'perc')\n% \n% Args:\n%   X: numeric array\n%   lo: scalar lower bound\n%   up: scalar upper bound\n%   'perc': use percentiles (0-100)\n% \n% See also: rescale, prctile\n\nif nargin > 2; bounds = prctile(X(:),bounds); end\n\nX2 = min(max(X,bounds(1)),bounds(2));\n\nend\n"
  },
  {
    "path": "leap/toolbox/utilities/functional_programming/wrap.m",
    "content": "function out = wrap(f, out_indices)\n\n% out = wrap(f, out_indices)\n% \n% If you've ever needed multiple outputs from a function wrapped up in a\n% cell array, you may have felt it was a bit awkward to accomplish without\n% doing something like this:\n%\n% [a, b, c] = f(...)\n% x = {a, b, c};\n%\n% That's not always desirable.\n%\n% This function makes it much easier. Just pass in a handle for a function\n% to execute and numbers indicating which outputs are desired in the cell \n% array.\n%\n% x = wrap(@() f(...), 1:3)\n%\n% This is especially useful for accessing multiple outputs from a function\n% *inside* an anonymous function, when one can't save numerous outputs as\n% in [a, b, c] = f(...).\n%\n% Example: \n%\n% info = wrap(@() min([4 3 2 3]), 1:2)\n%\n% Tucker McClure\n% Copyright 2013 The MathWorks, Inc.\n\n    [outs{1:max(out_indices)}] = f();\n    out = outs(out_indices);\n    \nend\n"
  },
  {
    "path": "leap/toolbox/utilities/get_new_string.m",
    "content": "function new_str = get_new_string(str, strings, format)\n%GET_NEW_STRING Returns a new string that does not exist in a provided list by incrementing a number.\n% Usage:\n%   new_str = get_new_string(str, strings)\n%   new_str = get_new_string(str, strings, format)\n% \n% Args:\n%   str: starting string\n%   strings: array of strings or cell array of chars\n%   format: string format to use (default: '%s_%d')\n% \n% See also: get_new_filename, get_new_varname\n\nif nargin < 3 || isempty(format); format = '%s_%d'; end\n\nnew_str = str;\ni = 0;\nwhile any(strcmp(new_str,strings))\n    i = i + 1;\n    new_str = sprintf(format, str, i);\nend\n\nend\n"
  },
  {
    "path": "leap/toolbox/utilities/grp2cell.m",
    "content": "function [C, grps, G] = grp2cell(X, G, dim)\n%GRP2CELL Splits an array into a cell using a grouping variable.\n% Usage:\n%   C = grp2cell(X, G)\n%   C = grp2cell(X, G, dim)\n%   [C, grps] = grp2cell(_)\n% \n% Args:\n%   X: array with at least one dimension of the same size as G\n%   G: grouping variable vector\n%   dim: dimension along which to slice X (default: [])\n%        if empty, first dimension of the same size as G is used\n%\n% Returns:\n%   C: cell array with as many cells as unique elements in G\n%   grps: values in G that correspond to the grouping in C\n%\n% Example:\n%   >> C = grp2cell(X,G,dim);\n%   >> isequal(cellcat(C, dim), X)\n%   ans =\n%     logical\n%      1\n% \n% See also: arr2cell, cellcat, celljoin\n\nif nargin < 3; dim = []; end\nif isscalar(G)\n    % Default to first non-singleton dimension\n    dim = find(size(X) > 1, 1);\n    if isempty(dim); dim = 1; end % or 1\n    \n    % Create grouping vector\n    G = ceil((1:size(X,dim)) / G);\nend\n\nif isempty(dim)\n    dim = find(size(X) == numel(G),1);\nend\n\nsubs = af(@(x) 1:x, size(X));\n\ngrps = unique(G);\nC = cell1(numel(grps),dim);\nfor i = 1:numel(grps)\n    subs_i = subs;\n    subs_i{dim} = find(G == grps(i));\n    \n    C{i} = X(subs_i{:});\nend\n\n\nend\n"
  },
  {
    "path": "leap/toolbox/utilities/horz.m",
    "content": "function V = horz(X)\n%HORZ Returns the array as a horizontal vector.\n% Usage:\n%   V = horz(X) % size(V) = [1, numel(X)]\n\nV = X(:)';\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/utilities/iseven.m",
    "content": "function TF = iseven(X)\n%ISEVEN Returns true if the input is divisible by 2, false otherwise.\n% Usage:\n%   TF = iseven(X)\n%\n% See also: mod, rem\n\nTF = logical(mod(X, 2) == 0);\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/utilities/loadvar.m",
    "content": "function varargout = loadvar(mat_file, var_name, varargin)\n%LOADVAR Loads one or more variables from a MAT file.\n% Usage:\n%   X = loadvar(mat_file) % returns first variable in MAT file\n%   X = loadvar(mat_file, var_name)\n%   [X1, X2, ..., XN] = loadvar(mat_file, var_name1, var_name2, ..., var_nameN)\n%   C = loadvar(mat_file, var_names) % variable names in cell; cell output\n%\n% Notes:\n%   - This is a shortcut for loading variables from MAT files without creating\n%   a temporary variable to contain the MAT file structure.\n%   - The MAT file does not need to have the .mat extension.\n% \n% See also: load, who\n\nif ~exists(mat_file); error('MAT file does not exist.'); end\n\nif nargin < 2\n    mat_vars = who('-file', mat_file);\n    var_name = mat_vars{1};\nend\n\n% Check variable names input\nvar_names = [var_name varargin];\nif ~iscellstr(var_names)\n    error('Variable names must be specified as strings.')\nend\n\n% Load variables from MAT file\nX = load(mat_file, '-mat', var_names{:});\n\n% Return output\nvarargout = cf(@(v) X.(v), var_names);\nif nargout ~= numel(varargout)\n    varargout = {varargout};\nend\nend\n\n"
  },
  {
    "path": "leap/toolbox/utilities/nunique.m",
    "content": "function N = nunique(X)\n%NUNIQUE Returns the number of unique elements in an array.\n% Usage:\n%   N = nunique(X)\n%\n% See also: unique\n\nN = numel(unique(X));\nend\n\n"
  },
  {
    "path": "leap/toolbox/utilities/rownorm.m",
    "content": "function n = rownorm(X, p)\n%ROWNORM Returns the row-wise norm of a matrix X.\n% Usage:\n%   n = rownorm(X)\n%   n = rownorm(X, p)\n% \n% Args:\n%   X: MxN numeric matrix\n%   p: degree of norm or 'fro' (default: 2)\n% \n% Returns:\n%   n: Mx1 vector of norms\n% \n% See also: norm, rownorm2\n\nif nargin < 2; p = 2; end\n\nn = sum(X.^p,2).^(1/p);\n\nend\n"
  },
  {
    "path": "leap/toolbox/utilities/stacks/imtile.m",
    "content": "function T = imtile(I, varargin)\n%IMTILE Tiles a stack or set of images.\n% Usage:\n%   T = imtile(stack)\n%   T = imtile(I1, I2, ...)\n%   T = imtile(stack, cols)\n% \n% Args:\n%   stack: 3-d, 4-d or cell array of images\n%   I1, I2, ...: 2-d images\n%   cols: number of columns to use\n%\n% Returns:\n%   T: single tiled image\n%\n% See also: catpadarr, montage\n\nif ~iscell(I) && ismatrix(I)\n   I = {I}; \nend\nif ndims(I) == 3\n    I = arr2cell(I,3);\nend\nif ndims(I) == 4\n    I = stack2cell(I);\nend\n\ncols = [];\nif nargin > 1\n    if isscalar(varargin{end})\n        cols = varargin{end};\n        varargin(end) = [];\n    end\n    I = [horz(I) varargin];\nend\n\nN = numel(I);\nif isempty(cols); cols = ceil(sqrt(N)); end\nrows = ceil(N / cols);\n\nif N < cols*rows\n    I((N+1):(cols*rows)) = {zeros(size(I{1}),'like',I{1})};\nend\n\nI = reshape(I,cols,rows)';\n\nT = af(@(r)cellcat(I(r,:),2),1:rows);\nT = cellcat(T,1);\n\nif nargout < 1; imgsc(T); clear T; end\n\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/utilities/stacks/stack2cell.m",
    "content": "function C = stack2cell(S)\n%STACK2CELL Returns a cell array with one slice of the stack in each cell.\n%   C = stack2cell(S)\n%\n% See also: cat, validate_stack, num2cell, mat2cell\n\nS = validate_stack(S);\n\nC = squeeze(num2cell(S,[1 2 3]));\n\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/utilities/stacks/stack2vecs.m",
    "content": "function X = stack2vecs(S)\n%STACK2VECS Convert stack to observation-by-features matrix.\n% Usage:\n%   X = stack2vecs(S)\n% \n% Args:\n%   S: 4-d array of size [h, w, c, n]\n% \n% Returns:\n%   X: 2-d array of size [n, (h * w * c)]\n% \n% See also: vecs2stack\n\nif ndims(S) == 3; S = permute(S,[1 2 4 3]); end\n\nX = reshape(S,[],size(S,4))';\n\nend\n"
  },
  {
    "path": "leap/toolbox/utilities/stacks/vecs2stack.m",
    "content": "function S = vecs2stack(X,sz)\n%VECS2STACK Convert observation-by-features matrix to stack.\n% Usage:\n%   X = stack2vecs(S)\n% \n% Args:\n%   X: 2-d array of size [n, (h * w * c)]\n%   sz: [h w c] or [h w c n]\n% \n% Returns:\n%   S: 4-d array of size [h, w, c, n]\n% \n% See also: stack2vecs\n\nif numel(sz) < 3; sz(3) = 1; end\nif numel(sz) < 4; sz(4) = size(X,1); end\n\nS = reshape(X',sz);\n\n\nend\n"
  },
  {
    "path": "leap/toolbox/utilities/swap.m",
    "content": "function [B, A] = swap(A, B)\n%SWAP Swap two variables without an intermediate copy.\n% Usage:\n%   [B, A] = swap(A, B)\n\nend"
  },
  {
    "path": "leap/toolbox/utilities/time/GetSystemTimePreciseAsFileTime.m",
    "content": "%GETSYSTEMTIMEPRECISEASFILETIME Returns system time with high precision.\n% Usage:\n%   t = GetSystemTimePreciseAsFileTime\n%\n% Returns:\n%   t: seconds from 00:00:00 UTC, 1/1/1601 with 0.1 ns tick precision\n%\n% Example:\n% >> a = GetSystemTimePreciseAsFileTime\n% a =\n%    1.3113e+10\n% >> b = GetSystemTimePreciseAsFileTime\n% b =\n%    1.3113e+10\n% >> b - a\n% ans =\n%     4.0448\n%\n% Reference:\n%   https://msdn.microsoft.com/en-us/library/windows/desktop/hh706895(v=vs.85).aspx\n%   https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx\n\n"
  },
  {
    "path": "leap/toolbox/utilities/time/stic.m",
    "content": "function timer_id = stic\n%STIC TIC equivalent using precise system time.\n% Usage:\n%   stic\n%   timer_id = tic\n%\n% See also: stoc, systime\n\nglobal stic_timers;\n\ntimer_id = numel(stic_timers) + 1;\nstic_timers(timer_id) = systime;\n\nif nargout < 1\n    clear timer_id\nend\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/utilities/time/stoc.m",
    "content": "function [dt, timer_id] = stoc(timer_id)\n%STIC TOC equivalent using precise system time.\n% Usage:\n%   stoc\n%   stoc(timer_id)\n%   [dt, timer_id] = stoc\n%\n% See also: stic, systime\n\nglobal stic_timers;\n\nif nargin < 1\n    timer_id = numel(stic_timers);\nend\n\nif numel(stic_timers) == 0\n    error('You must call STIC before calling this function.')\nend\n\nif ~isscalar(timer_id) || timer_id ~= round(timer_id) || timer_id > numel(stic_timers) || timer_id < 1\n    error('Invalid timer ID specified.')\nend\n\ndt = systime - stic_timers(timer_id);\n% stic_timers(timer_id) = [];\n\nif nargout == 0\n    fprintf('[%d] Elapsed time is %f seconds.\\n', timer_id, dt)\n    clear dt\nend\nif nargout < 2\n    clear timer_id\nend\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/utilities/time/stocf.m",
    "content": "function [dt, timer_id] = stocf(timer_id, str, varargin)\n%STOCF Report elapsed time with print formatting.\n% Usage:\n%   stocf(str, ...)\n%   stocf(timer_id, str, ...)\n%   [dt, timer_id] = stocf(_)\n%\n% See also: stic, stoc\n\n% Get elapsed time\nif ischar(timer_id)\n    if nargin > 1; varargin = [{str} varargin]; end\n    % No timer specified, so just use last\n    str = timer_id;\n    [dt, timer_id] = stoc;\nelse\n    dt = stoc(timer_id);\nend\n\n% Figure out where to plug in the elapsed time\ndt_idx = find(areempty(varargin),1,'last');\n\n% Use default formatting if no empty arrays specified\nif isempty(dt_idx)\n    dt_idx = numel(varargin) + 1;\n    if str(end) ~= ' '; str(end+1) = ' '; end % pad with space\n    if dt > 5*60 % after 5 mins, use string representation\n        str = [str '[' secsf(dt) ']'];\n    else\n        % Put elapsed time in argument list\n        str = [str '[%.2fs]'];\n        varargin{dt_idx} = dt;\n    end\nend\n\n% Print!\nprintf(str, varargin{:})\n\n% No returns if no output requested\nif nargout == 0\n    clear dt timer_id\nend\nend\n\n"
  },
  {
    "path": "leap/toolbox/utilities/time/systime.m",
    "content": "function secs = systime\n%SYSTIME Returns precise system time in seconds.\n% Usage:\n%   secs = systime\n%\n% Note: This is just a wrapper for GetSystemTimePreciseAsFileTime\n%\n% See also: GetSystemTimePreciseAsFileTime\n\nif ispc\n    [~,win_ver] = system('ver');\n    % Ref: https://en.wikipedia.org/wiki/Ver_(command)\n    if contains(win_ver,'Version 10')\n        secs = GetSystemTimePreciseAsFileTime;\n        return\n    end\nend\n\n% Fallback\nsecs = now * 86400 - 50522817600; % same units\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/utilities/varsize.m",
    "content": "function size = varsize(X, units)\n%VARSIZE Returns the size of a variable in bytes.\n% Usage:\n%   size = varsize(X)\n%   size = varsize(X, units) % 'bytes' (default), 'KB', 'MB', 'GB'\n%\n% See also: whos\nnarginchk(1, 2)\nif nargin < 2\n    units = 'bytes';\nend\n\n% Get variable size in bytes\nS = whos('X');\nsize = S.bytes;\n\nif nargout < 1\n    % Just output to console if not storing variable\n    disp(bytes2str(size, 4))\n    clear size\n    return\nend\n\n% Convert to units\nswitch lower(units)\n    case {'kb', 'k', 'kilo', 'kilobyte', 'kib'}\n        size = size / 1024;\n    case {'mb', 'm', 'mega', 'megabyte', 'mib'}\n        size = size / 1024 / 1024;\n    case {'gb', 'g', 'giga', 'gigabyte', 'gib'}\n        size = size / 1024 / 1024 / 1024;\n    case {'auto', 'str'}\n        size = bytes2str(size, 4);\n    otherwise\n        return\nend\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/utilities/varstruct.m",
    "content": "function S = varstruct(var1, var2, varargin)\n%VARSTRUCT Creates a structure out of a set of variables. Fieldnames are inferred from variable names.\n% Usage:\n%   S = varstruct(var1, var2, ...)\n%   S = varstruct('field1', val1, 'field2', val2, ...) % same as struct()\n% \n% Args:\n%   var1, var2, ...: variables used to create the struct\n%       If the variables are defined (see inputname) rather than just\n%       passed by value, their workspace names are used as fieldnames.\n%   'field1', val1, 'field2', val2, ...: name-value pairs like struct()\n%   \n% Returns:\n%   S: created structure\n%\n% Note: The two forms of input can be used in the same call.\n%       Variables without a name will be named 'varN' according to their\n%       position in the argument list.\n%\n% Example:\n% >> S = varstruct(x, y, 'a', 3, 5, 5)\n% S = \n%   struct with fields:\n% \n%        x: [55 double]\n%        y: 2\n%        a: 3\n%     var5: 5\n%     var6: 5\n%   \n% See also: cell2struct, struct, inputname\n\n% Get input names\nN = nargin;\nnames = cell(1,N);\nfor i = 1:N\n    names{i} = inputname(i);\nend\n\n% Concatenate all args\nargs_in = {var1};\nif N > 1; args_in{end+1} = var2; end\nargs_in = [args_in varargin];\n\n% Let's take care of the defined variables first\nnoName = areempty(names);\nargs = horz([names(~noName); args_in(~noName)]);\n\n% Now let's take care of name-val pairs\nisChar = cellfun(@ischar, args_in);\nisName = isChar(1:end-1) & noName(1:end-1) & noName(2:end); % names in name-val pairs are also not named\nisVal = [false isName];  % val cannot be first\nisName = [isName false]; % name cannot be last\nif any(isName)\n    name_vals = horz([args_in(isName); args_in(isVal)]);\n    args = [args name_vals];\nend\n\n% Now let's take care of unnamed args\nisUnnamed = noName & ~(isName | isVal);\nif any(isUnnamed)\n    orderedNames = strcat('var', strsplit(num2str(find(isUnnamed))));\n    unnamed_args = horz([orderedNames; args_in(isUnnamed)]);\n    args = [args unnamed_args];\nend\n\n% Now just create the structure!\n% S = struct(args{:});\nS = cell2struct(args(2:2:end),args(1:2:end),2); % prevents cell array issues\n\nend"
  },
  {
    "path": "leap/toolbox/utilities/vert.m",
    "content": "function V = vert(X)\n%VERT Returns the matrix as a vertical vector.\n% Usage:\n%   V = vert(X)\n%\n% See also: mat2vec, reshape\n\nV = X(:);\n\nend\n\n"
  },
  {
    "path": "leap/toolbox/utilities/vplay.m",
    "content": "function vp = vplay(mov, varargin)\n%VPLAY Play a movie using the vplayer class.\n% Usage:\n%   vplay(mov)\n%   vplay(mov, ...) % name-val params\n%   vp = vplay(_)\n% \n% Args:\n%   mov: 4-d stack, cell array of 2-d images or ind-val structure\n% \n% See also: vplayer\n\nvp = vplayer(mov, varargin{:});\n\nif nargout < 1; clear vp; end\nend\n"
  },
  {
    "path": "leap/toolbox/video/validate_stack.m",
    "content": "function [images, numFrames] = validate_stack(images, no_error)\n%VALIDATE_STACK Validates a stack of images and converts to 4-D array.\n% Usage:\n%   images = validate_stack(images)\n%   [images, numFrames] = validate_stack(images)\n%   _ = validate_stack(images, true) % doesn't error if invalid stack\n%\n% Returns a WxHxCxF array.\nif nargin < 2; no_error = false; end\n\nif isstruct(images) && isfield(images, 'cdata') % getframe struct array\n    images = af(@(x) frame2im(x), images);\n    images = cat(4, images{:});\nend\nif iscell(images)\n    images = cat(4, images{:});\nend\nif ndims(images) == 3\n    images = permute(images, [1 2 4 3]);\nend\nif ndims(images) ~= 4\n    if ~no_error\n        error('Images must be an MxNxCxF array.')\n    else\n        warning('Images must be an MxNxCxF array.')\n    end\nend\nnumFrames = size(images,4);\n    \nend\n\n"
  },
  {
    "path": "leap/toolbox/video/vplayer.m",
    "content": "classdef vplayer < matlab.mixin.SetGet\n    %VPLAYER Image video/stack player class.\n    % Usage:\n    %   vplayer(S)\n    %\n    %   See also: imstacksc\n    \n    properties (SetAccess = immutable)\n        numFrames, numChannels, frameSize\n    end\n    properties\n        S % stack\n        matField % variable name of stack in MatFile\n        getFrameFcn % function that returns frame\n        ndims = 4\n        \n        fig, ax, img % graphics objects\n        ui = struct() % uix components\n        closeWithPlayer = {} % other graphics object to delete when closing\n        clims, cmap % color scaling\n        title = ''\n        scale = true % scale to colormap\n        \n        frame % current frame\n        idx % current frame index within S\n        channel % current channel\n        \n        doCache = false % whether caching is enabled\n        cached % logical indicator\n        cache % cell array for storage\n        \n        isPlaying = false\n        fps = 25\n        stride = 1\n        pb_timer\n        \n        doSave = false % set to true to save frames on draw\n        saved = {}\n        \n        params = struct()\n        callbacks = {}\n        hotkeys = [] %table([],[],[],'VariableNames',{'Key','Modifiers','Callback'})\n        bookmarks = [] % indices\n        seekbar\n    end\n    \n    events\n        NewFrame\n    end\n    \n    methods\n        function h = vplayer(S, varargin)\n            % Input formats\n            if iscell(S); S = cellcat(S,4); end\n            if ischar(S)\n                if endsWith(S,'.mat')\n                    S = matfile(S);\n                elseif endsWith(S,'fg.h5')\n                    S = h5file(S,'/fg');\n                elseif endsWith(S,'processed.h5')\n                    S = h5file(S,'/video/data');\n                elseif endsWith(S,'.h5') && nargin > 1 && varargin{1}(1) == '/'\n                    S = h5file(S,varargin{1});\n                    varargin(1) = [];\n                elseif endsWith(S,'.h5')\n                    dsets = h5getdatasets(S);\n                    if any(strcmp(dsets,'/box'))\n                        S = h5file(S,'/box');\n                    end\n                end\n            end\n%             if ischar(S) && strcmpi(get_ext(S),'.mat'); S = matfile(S); end\n%             if ischar(S) && strcmpi(get_ext(S),'.h5')\n%                 if strcmpi(get_filename(S),'processed.h5') || endsWith(get_filename(S),'.lz4.h5')\n%                     S = h5file(S,'/video/data');\n%                 elseif strcmpi(get_filename(S),'fg.h5')\n%                     S = h5file(S,'/fg');\n%                 end\n%             end\n            h.S = S;\n            def_stackName = inputname(1);\n            default_clims = [];\n            switch class(h.S)\n                case 'matlab.io.MatFile' % assumes 4-D numeric stack\n                    % Check inputs\n                    if nargin < 2 || ~ischar(varargin{1})\n                        error(['Variable name must be specified if playing from MatFile.\\\\n' ...\n                            '   Example: vplayer(matfile(path),''mov'')']);\n                    end\n                    h.matField = varargin{1};\n                    varargin(1) = [];\n                    \n                    % Check variable name\n                    vars = who(h.S);\n                    if ~ismember(h.matField,vars); error('Variable ''%s'' is not in MatFile.', h.matField); end\n                    \n                    % Check size\n                    sz = size(h.S,h.matField);\n                    if numel(sz) ~= 4; error('Stack variable ''%s'' must be numeric 4-D array.', h.matField); end\n                    \n                    % Setup\n                    h.numFrames = sz(4);\n                    h.getFrameFcn = @getFrame_MatFile;\n                    h.doCache = true;\n                    def_stackName = h.matField;\n                case {'hdf5prop','hdf5file'}\n                    % Ref: http://www.mathworks.com/matlabcentral/fileexchange/31703-hdf5-diskmap-class\n                    sz = size(h.S);\n                    h.ndims = numel(sz);\n                    h.numFrames = sz(end);\n                    h.getFrameFcn = @getFrame_stack;\n                    h.doCache = false; % hdf5prop does caching already\n                    def_stackName = h.S.dataset;\n                case 'struct'\n                    fields = fieldnames(h.S);\n                    if ~any(ismember({'sz','size'},fields)) || ...\n                        ~any(ismember({'ind','idx'},fields))\n                        error('Structure array must have ''idx'' and ''size'' fields (see ind2im).')\n                    end\n                    h.numFrames = numel(h.S);\n                    h.getFrameFcn = @getFrame_struct;\n                    h.doCache = true;\n                otherwise\n                    if ~isnumeric(h.S) && ~islogical(h.S); error('Invalid stack format specified.'); end\n                    if ndims(h.S) == 3; h.S = permute(h.S, [1 2 4 3]); end\n                    if ndims(h.S) < 4; error('Numeric stack must be 4-D array.'); end\n                    default_clims = alims(h.S);\n                    h.numFrames = size(h.S,4);\n                    h.getFrameFcn = @getFrame_stack;\n            end\n            \n            % Initialize caching\n            h.cached = false(1,h.numFrames);\n            h.cache = cell(1,h.numFrames);\n            \n            % Initialize saving container\n            h.saved = cell(1,h.numFrames);\n            \n            % Shortcut args\n            default_cmap = [];\n            if numel(varargin) > 0\n                p = varargin{1};\n                if ischar(p) && any(strcmpi(p,{'parula','gray','jet','hot'}))\n                    default_cmap = p;\n                    varargin(1) = [];\n                end\n            end\n            if numel(varargin) > 0\n                p = varargin{1};\n                \n                % Parse frame draw callbacks from args\n                if iscell(p) && ~isempty(p) && all(cellfun(@(x)isa(x, 'function_handle'),p)) % callbacks\n                    h.callbacks = p;\n                    varargin(1) = [];\n                elseif isa(p, 'function_handle') % single callback\n                    h.callbacks = {p};\n                    varargin(1) = [];\n                end\n            end\n            \n            % Parameters\n            defaults = struct();\n            defaults.colormap = default_cmap;\n            defaults.clims = default_clims;\n            defaults.scale = []; % heuristic default below\n            defaults.stackName = def_stackName;\n            defaults.position = [];\n            defaults.initialFrame = 1;\n            defaults.initialChannel = 1;\n            defaults.autoplay = false;\n            defaults.fps = 25;\n            defaults.stride = 1;\n            defaults.save = false;\n            defaults.saveTarget = 'ax'; % 'fig' or 'ax'\n            defaults.clear = true; % clear graphics objects on axes between frames\n            defaults.colorbar = false;\n            defaults.tight = true;\n            defaults.autotile = false; % tiles channels\n            defaults.bookmarks = [];\n            defaults.seekbar = false;\n            defaults.zoom = 1;\n            h.params = parse_params(varargin, defaults);\n            \n            \n            if isempty(h.params.colormap)\n                if isa(h.S,'uint8') || isa(h.S,'hdf5prop') || isa(h.S,'hdf5file')\n                    h.params.colormap = 'gray';\n                else\n                    h.params.colormap = 'parula';\n                end\n            end\n            \n            % Update properties with params\n            h.cmap = h.params.colormap;\n            h.doSave = h.params.save;\n            h.fps = h.params.fps;\n            h.stride = h.params.stride;\n            h.bookmarks = h.params.bookmarks;\n            h.seekbar = h.params.seekbar;\n            \n            % Load first frame\n            h.frame = h.getFrame(h.params.initialFrame);\n            \n            % Image metadata\n            h.numChannels = size(h.frame,3);\n            h.frameSize = size(h.frame(:,:,1));\n            h.idx = h.params.initialFrame;\n            h.channel = h.params.initialChannel;\n            \n            % Set color limits\n            h.clims = h.params.clims;\n            if isempty(h.clims)\n                if isa(h.frame,'uint8')\n                    h.clims = [0 255];\n                elseif isfloat(h.frame) && (isa(h.S,'hdf5prop') || isa(h.S,'hdf5file'))\n                    h.clims = [0 1];\n                else\n                    h.clims = alims(h.frame);\n                end\n            end\n            if ~isnumeric(h.clims); h.clims = double(h.clims); end\n            \n            % Color scaling\n            if isempty(h.params.scale)\n                h.scale = h.numChannels ~= 3; % size(S,3) == 3\n                h.channel = 1:h.numChannels;\n            else\n                % User provided parameter\n                h.scale = h.params.scale;\n            end\n            \n            % Add hotkeys\n            h.addHotkey('leftarrow', @()h.seek(-1))\n            h.addHotkey('rightarrow', @()h.seek(+1))\n            h.addHotkey('leftarrow', @()h.seek(-5),'shift')\n            h.addHotkey('rightarrow', @()h.seek(+5),'shift')\n            h.addHotkey('leftarrow', @()h.seek(-50),'control')\n            h.addHotkey('rightarrow', @()h.seek(+50),'control')\n            h.addHotkey('leftarrow', @()h.seek(-500),{'control','shift'})\n            h.addHotkey('rightarrow', @()h.seek(+500),{'control','shift'})\n            h.addHotkey('leftarrow', @()h.prevBookmark,'alt')\n            h.addHotkey('rightarrow', @()h.nextBookmark,'alt')\n%             h.addHotkey('space', @()play(h,true))\n            h.addHotkey('space', @()h.play())\n            h.addHotkey('uparrow',   @()set(h,'fps',h.fps + 5))\n            h.addHotkey('downarrow', @()set(h,'fps',h.fps - 5))\n            \n            h.addHotkey('add', @()set(h,'stride',h.stride + 1))\n            h.addHotkey('subtract', @()set(h,'stride',h.stride - 1))\n            h.addHotkey('uparrow', @()set(h,'stride',h.stride + 1),'shift')\n            h.addHotkey('downarrow', @()set(h,'stride',h.stride - 1),'shift')\n            \n            h.addHotkey('add', @()h.zoom(2),'control')\n            h.addHotkey('subtract', @()h.zoom(0.5),'control')\n            h.addHotkey('uparrow', @()h.zoom(2),'control')\n            h.addHotkey('downarrow', @()h.zoom(0.5),'control')\n            \n            h.addHotkey('pageup', @()set(h,'channel',mod(h.channel-1 +1,h.numChannels)+1))\n            h.addHotkey('pagedown', @()set(h,'channel',mod(h.channel-1 -1,h.numChannels)+1))\n            h.addHotkey('uparrow', @()set(h,'channel',mod(h.channel-1 +1,h.numChannels)+1),'alt')\n            h.addHotkey('downarrow', @()set(h,'channel',mod(h.channel-1 -1,h.numChannels)+1),'alt')\n            \n            h.addHotkey('home', @()plot(h,1))\n            h.addHotkey('end', @()plot(h,h.numFrames))\n            h.addHotkey('a', @()autoAdjust(h,true)) % frame\n            h.addHotkey('a', @()autoAdjust(h,false),'shift') % default\n            h.addHotkey('a', @()stretchLims(h),'control') % stretchlim\n            h.addHotkey('q', @()delete(h.fig))\n            \n            % Initialize graphics\n            h.show();\n            \n            % Redraw initial frame to trigger callbacks and etc.\n            h.plot(h.idx);\n            \n            % Initial zoom\n            h.zoom(h.params.zoom);\n            h.plot(h.idx);\n            \n            h.pb_timer = timer(...\n                'BusyMode','drop',...\n                'ExecutionMode','fixedRate',...\n                'Name','vp_playback',...\n                'Period',1/h.fps,...\n                'TimerFcn',@(tmr,evt)pb_callback(h,tmr,evt) ...\n                );\n            \n        end\n        \n        function show(h)\n            if ishghandle(h.fig); delete(h.fig); end\n            \n            % Show figure\n            h.fig = figure('KeyPressFcn', @(~,evt)KeyPressCB(h,evt), ...\n                'CloseRequestFcn', @(~,~)OnClose(h), ...\n                'DeleteFcn', @(~,~)OnClose(h), ...\n                'NumberTitle', 'off', 'Name', h.params.stackName);\n            borderStyle = 'loose'; if h.params.tight; borderStyle = 'tight'; end\n            \n            h.ui.grid = uix.GridFlex('Parent', h.fig, 'Spacing', 1);\n            \n            h.ax = axes('Parent',uicontainer('Parent',h.ui.grid,'BackgroundColor',[0 0 0]));\n            h.ui.img_ax = h.ax;\n            h.img = imshow(h.frame, 'Border',borderStyle, 'Parent',h.ax);\n            h.ui.img_h = h.img;\n            \n            if h.seekbar\n                h.ui.seekbar_ax = axes('Parent',uicontainer('Parent',h.ui.grid));\n                h.ui.seekbar_img = imagesc(zeros(1,h.numFrames,'uint8'),'Parent',h.ui.seekbar_ax);\n            \n                noticks(h.ui.seekbar_ax);\n            end\n            function constrained_pos = seekbar_constraint(new_pos)\n                constrained_pos = new_pos;\n                constrained_pos(:,1) = min(max(constrained_pos(:,1),0.5),h.numFrames+0.5);\n                constrained_pos(:,2) = [0.5 1.5];\n                constrained_pos(:,1) = round(mean(constrained_pos(:,1)));\n            end\n            function seekbar_update(new_pos)\n                h.plot(max(1,min(round(new_pos(1)),h.numFrames)));\n            end\n            if h.seekbar\n                h.ui.seekbar_line = imline(h.ui.seekbar_ax, [1 1], [0.5 1.5], ...\n                    'PositionConstraintFcn',@seekbar_constraint);\n                h.ui.seekbar_line.setColor('r');\n                h.ui.seekbar_line.addNewPositionCallback(@seekbar_update);\n                h.callbacks{end+1} = @(h,idx)h.ui.seekbar_line.setConstrainedPosition([idx 0.5; idx 1.5]);\n            end\n%             screen_size = get(0,'ScreenSize');\n%             set(h.ui.grid, 'Widths', [-1], 'Heights', [min(screen_size(end),h.frameSize(1)) 20]);\n            if h.seekbar\n                set(h.ui.grid, 'Widths', [-1], 'Heights', [-1 20]);\n            else\n                set(h.ui.grid, 'Widths', [-1], 'Heights', [-1]);\n            end\n            \n            if h.scale\n                caxis(h.ax,h.clims);\n                colormap(h.ax, h.cmap);\n            end\n            if ~isempty(h.params.position); h.fig.Position = h.params.position; end\n            if h.params.colorbar; h.ui.img_colorbar = colorbar(h.ax); end\n            \n            if h.seekbar\n                caxis(h.ui.seekbar_ax,[0 1]);\n                colormap(h.ui.seekbar_ax, 'parula');\n                h.ui.seekbar_ax.Units = 'normalized';\n                h.ui.seekbar_ax.Position = [0 0 1 1];\n            end\n            h.ax.Units = 'normalized';\n            h.ax.Position = [0 0 1 1];\n            \n            if h.seekbar\n                seekbar_pos = getpixelposition(h.ui.seekbar_ax.Parent);\n                h.fig.Position(4) = h.fig.Position(4) + seekbar_pos(4);\n            end\n            \n        end\n        \n        function hide(h)\n            if ishghandle(h.fig); delete(h.fig); end\n        end\n        function set.scale(h,val)\n            if isempty(val); return; end\n            h.scale = val;\n            if ~isempty(h.img) && ishghandle(h.img)\n                if h.scale\n                    set(h.img,'CDataMapping','scaled');\n                else\n                    h.channel = 1:h.numChannels;\n                    set(h.img,'CDataMapping','direct');\n                end\n            end\n        end\n        function set.clims(h,val)\n            if isempty(val); return; end\n            if ~isnumeric(val); val = double(val); end\n            h.clims = val;\n            if ~isempty(h.ax) && ishghandle(h.ax) && numel(h.clims) == 2\n                caxis(h.ax,h.clims);\n            end\n        end\n        function autoAdjust(h,frameOnly)\n            if nargin < 2; frameOnly = true; end\n            if frameOnly\n                h.clims = alims(h.frame);\n            else\n                h.clims = h.params.clims;\n            end\n        end\n        function stretchLims(h)\n%             disp(alims(h.frame))\n%             disp(horz(stretchlim(h.frame)))\n            lims = horz(stretchlim(h.frame));\n            if isa(h.frame,'uint8'); lims = lims .* 255; end\n            h.clims = lims;\n        end\n        \n        \n        function zoom(h,factor,relativeToFull)\n            if nargin < 2; factor = 1; end\n            if nargin < 3; relativeToFull = false; end\n            \n            pos = h.fig.Position;\n            if relativeToFull\n                new_sz = round(h.frameSize([2 1]) .* factor);\n            else\n                new_sz = round(pos(3:4) .* factor);\n            end\n            delta = pos(3:4) - new_sz;\n            \n%             h.fig.Position = h.fig.Position - [-delta delta];\n%             h.fig.Position = h.fig.Position - [0 -delta(2) delta];\n            h.fig.Position = h.fig.Position - [-delta(1)/2 -delta(2) delta];\n%             h.fig.Position = h.fig.Position - [0 0 delta];\n            h.fig.Position(1:2) = max(1, h.fig.Position(1:2));\n        end\n        \n        \n        function frame = getFrame(h, idx)\n            if h.doCache && h.cached(idx) && ~isempty(h.cache{idx})\n               frame = h.cache{idx};\n               return\n            end\n            \n            % Get frame\n            frame = h.getFrameFcn(h,idx);\n            \n            % Save to cache\n            if h.doCache\n                h.cached(idx) = true;\n                h.cache{idx} = frame;\n            end\n        end\n        function frame = getFrame_stack(h, idx)\n            if h.ndims == 3\n                frame = h.S(:,:,idx);\n            else\n                frame = h.S(:,:,:,idx);\n            end\n        end\n        function frame = getFrame_MatFile(h, idx)\n            if h.ndims == 3\n                frame = h.S.(h.matField)(:,:,idx);\n            else\n                frame = h.S.(h.matField)(:,:,:,idx);\n            end\n        end\n        function frame = getFrame_struct(h, idx)\n            frame = ind2im(h.S(idx));\n        end\n        \n        function clearCache(h)\n            h.cache = cell(1,h.numFrames);\n            h.cached = false(1,h.numFrames);\n        end\n        \n        function redraw(h)\n            h.plot(h.idx);\n        end\n        function plot(h, idx)\n            % Update frame index\n            h.idx = mod(idx-1,h.numFrames)+1;\n            \n            % Get frame image\n            h.frame = h.getFrame(h.idx);\n            if h.params.autotile, h.frame = imtile(h.frame);\n            else; h.frame = h.frame(:,:,h.channel); end\n            \n            % Update image\n            h.img.CData = h.frame;\n            \n            % Clear old graphics\n            if h.params.clear\n%                 isImage = arrayfun(@(x) isa(x,'matlab.graphics.primitive.Image'), h.ax.Children);\n                delete(h.ax.Children(h.ax.Children ~= h.img))\n            end\n            \n            % Update title\n            h.updateTitle();\n            \n            % Invoke frame drawing callbacks\n            h.fig.CurrentAxes = h.ax;\n            hold(h.ax,'on');\n            for j = 1:numel(h.callbacks)\n                cb = h.callbacks{j};\n                % Pass inputs\n                if nargin(cb) == 1\n                    args = {h.frame};\n                elseif nargin(cb) == 2\n                    args = {h, h.idx};\n                else\n                    args = {h, h.idx, h.frame};\n                end\n                \n                % Collect outputs\n                if nargout(cb) < 1 % simple callback\n                    cb(args{:});\n                else % update frame if there's anything returned\n                    h.frame = cb(args{:});\n                    h.img.CData = h.frame;\n                end\n            end\n\n            % Redraw graphics\n            drawnow;\n            \n            % Save frame\n            if h.doSave\n                if ischar(h.params.saveTarget)\n                    switch h.params.saveTarget\n                        case 'fig'\n                            h.params.saveTarget = h.fig;\n                        case 'ax'\n                            h.params.saveTarget = h.ax;\n                    end\n                end\n                h.saved{idx} = frame2im(getframe(h.params.saveTarget));\n            end\n            \n            % Fire event\n            h.notify('NewFrame');\n        end\n        \n        % Playback\n%         function play(h,toggle)\n%             if nargin < 2; toggle = true; end\n%             \n%             if toggle\n%                 h.isPlaying = ~h.isPlaying;\n%             else\n%                 h.isPlaying = true;\n%             end\n%             \n%             while ishghandle(h.fig) && h.isPlaying\n%                 % Draw next frame\n%                 h.plot(h.idx + h.stride)\n% \n%                 % Wait\n% %                 pause(h.stride / h.fps)\n%                 pause(1 / h.fps)\n%             end\n%         end\n        \n        function play(h,~)\n            if h.isPlaying\n                h.isPlaying = false;\n%                 h.pb_timer.stop();\n            else\n                h.isPlaying = true;\n                start(h.pb_timer);\n            end\n        end\n        function pb_callback(h,tmr,evt)\n            if h.isPlaying\n                h.seek(h.stride);\n            else\n                stop(tmr);\n            end\n        end\n            \n        function stop(h)\n            h.isPlaying = false;\n        end\n        function playTo(h,stop_idx)\n            % Plays until specified frame and then stops\n            if nargin < 2; stop_idx = h.numFrames; end\n            if h.isPlaying; h.play(); end % stop if already playing\n            for i = h.idx:h.stride:stop_idx\n                h.plot(i);\n            end\n        end\n        function goTo(h,idx)\n            % Wrapper for h.plot\n            h.plot(idx);\n        end\n        function seek(h,delta)\n            new_idx = h.idx + delta;\n            new_idx = mod(new_idx-1,h.numFrames)+1;\n            h.plot(new_idx);\n        end\n        function nextBookmark(h)\n            isAfter = h.bookmarks > h.idx;\n            if any(isAfter)\n                next_idx = h.bookmarks(find(isAfter,1));\n                h.plot(next_idx);\n            end\n        end\n        function prevBookmark(h)\n            isBefore = h.bookmarks < h.idx;\n            if any(isBefore)\n                prev_idx = h.bookmarks(find(isBefore,1,'last'));\n                h.plot(prev_idx);\n            end\n        end\n        \n        function set.fps(h,val)\n            h.fps = max(val,0);\n            if ishghandle(h.fig)\n                if h.fps == 0; h.stop(); else; h.play(false); end\n            end\n        end\n        function set.stride(h,val)\n            h.stride = max(val,1);\n            if ishghandle(h.fig); h.play(false); end\n        end\n        function set.channel(h,val)\n            h.channel = mod(val-1,h.numChannels) + 1;\n            if ishghandle(h.fig); h.redraw(); end\n        end\n        \n        \n        function updateTitle(h)\n            newTitle = sprintf('%d/%d', h.idx, h.numFrames);\n            \n            if h.numChannels > 1 && ~h.params.autotile && h.scale\n                newTitle = sprintf('%s (C: %d/%d)', newTitle, h.channel, h.numChannels);\n            end\n            \n            if h.doSave\n                newTitle = sprintf('%s | Saved: %d', newTitle, sum(~cellfun(@isempty,h.saved)));\n            end\n            \n            if h.isPlaying\n                newTitle = sprintf('%s | FPS: %d | Stride: %d', ...\n                    newTitle, h.fps, h.stride);\n            end\n            \n            h.title = newTitle;\n            if ~isempty(h.params.stackName)\n                h.fig.Name = [h.params.stackName ': ' newTitle];\n            else\n                h.fig.Name = newTitle;\n            end\n        end\n        \n        function KeyPressCB(h, evt)\n            isKey = strcmpi(h.hotkeys.Key, evt.Key);\n            if isempty(evt.Modifier)\n                isMod = areempty(h.hotkeys.Modifiers);\n            else\n                isMod = cellfun(@(x) isequal(sort(evt.Modifier),sort(x)), h.hotkeys.Modifiers);\n            end\n            \n            if any(isKey & isMod)\n                f = h.hotkeys.Callback{isKey & isMod};\n                \n                if nargin(f) == 0; f();\n                else; f(h,h.idx); end\n            else\n                if ~contains(evt.Key,evt.Modifier) && ~contains(evt.Key,'windows')\n                    printf('No hotkey bound for: %s', evt.Key)\n                end\n%                 printf('No hotkey bound for: %s + %s', evt.Modifier, evt.Key)\n            end\n        end\n        function addHotkey(h, key, fun, modifiers)\n            if nargin < 4; modifiers = {}; end\n            if ~iscell(modifiers); modifiers = {modifiers}; end\n            \n            hotkey = cell2table({key,{modifiers},fun},'VariableNames',{'Key','Modifiers','Callback'});\n            if isempty(h.hotkeys)\n                h.hotkeys = hotkey;\n            else\n                h.hotkeys = [h.hotkeys; hotkey];\n            end\n            \n%             h.hotkeys.(key) = fun;\n        end\n%         function rmHotkey(h, key)\n%             h.hotkeys = rmfield(h.hotkeys, key);\n%         end\n        \n        function closeWith(h,h_fig)\n            % Add another figure to close when the player closes\n            h.closeWithPlayer{end+1} = h_fig;\n        end\n        function OnClose(h)\n            % Prevent error from quitting while playing\n            h.isPlaying = false;\n            for i = 1:numel(h.closeWithPlayer)\n                delete(h.closeWithPlayer{i})\n            end\n            delete(h.fig)\n            delete(h)\n        end\n    end\n    \nend\n\n"
  },
  {
    "path": "leap/training.py",
    "content": "import numpy as np\nimport h5py\nimport os\nfrom time import time\nfrom scipy.io import loadmat, savemat\nimport re\nimport shutil\nimport clize\n\nimport keras\nfrom keras.callbacks import ReduceLROnPlateau, ModelCheckpoint, LambdaCallback\n\nfrom leap import models\nfrom leap.image_augmentation import PairedImageAugmenter, MultiInputOutputPairedImageAugmenter\nfrom leap.viz import show_pred, show_confmap_grid, plot_history\nfrom leap.utils import load_dataset\n\n\ndef train_val_split(X, Y, val_size=0.15, shuffle=True):\n    \"\"\" Splits datasets into training and validation sets. \"\"\"\n\n    if val_size < 1:\n        val_size = int(np.round(len(X) * val_size))\n\n    idx = np.arange(len(X))\n    if shuffle:\n        np.random.shuffle(idx)\n\n    val_idx = idx[:val_size]\n    idx = idx[val_size:]\n\n    return X[idx], Y[idx], X[val_idx], Y[val_idx], idx, val_idx\n\n\ndef create_run_folders(run_name, base_path=\"models\", clean=False):\n    \"\"\" Creates subfolders necessary for outputs of training. \"\"\"\n\n    def is_empty_run(run_path):\n        weights_path = os.path.join(run_path, \"weights\")\n        has_weights_folder = os.path.exists(weights_path)\n        return not has_weights_folder or len(os.listdir(weights_path)) == 0\n\n    run_path = os.path.join(base_path, run_name)\n\n    if not clean:\n        initial_run_path = run_path\n        i = 1\n        while os.path.exists(run_path): #and not is_empty_run(run_path):\n            run_path = \"%s_%02d\" % (initial_run_path, i)\n            i += 1\n\n    if os.path.exists(run_path):\n        shutil.rmtree(run_path)\n\n    os.makedirs(run_path)\n    os.makedirs(os.path.join(run_path, \"weights\"))\n    os.makedirs(os.path.join(run_path, \"viz_pred\"))\n    os.makedirs(os.path.join(run_path, \"viz_confmaps\"))\n    print(\"Created folder:\", run_path)\n\n    return run_path\n\n\n\nclass LossHistory(keras.callbacks.Callback):\n    def __init__(self, run_path):\n        super().__init__()\n        self.run_path = run_path\n\n    def on_train_begin(self, logs={}):\n        self.history = []\n\n    def on_epoch_end(self, batch, logs={}):\n        # Append to log list\n        self.history.append(logs.copy())\n\n        # Save history so far to MAT file\n        savemat(os.path.join(self.run_path, \"history.mat\"),\n                {k: [x[k] for x in self.history] for k in self.history[0].keys()})\n\n        # Plot graph\n        plot_history(self.history, save_path=os.path.join(self.run_path, \"history.png\"))\n\n\ndef create_model(net_name, img_size, output_channels, **kwargs):\n    \"\"\" Wrapper for initializing a network for training. \"\"\"\n    # compile_model = getattr(models, net_name)\n\n    compile_model = dict(\n        leap_cnn=models.leap_cnn,\n        hourglass=models.hourglass,\n        stacked_hourglass=models.stacked_hourglass,\n        ).get(net_name)\n    if compile_model == None:\n        return None\n\n    return compile_model(img_size, output_channels, **kwargs)\n\ndef train(data_path, *,\n    base_output_path=\"models\",\n    run_name=None,\n    data_name=None,\n    net_name=\"leap_cnn\",\n    clean=False,\n    box_dset=\"box\",\n    confmap_dset=\"confmaps\",\n    val_size=0.15,\n    preshuffle=True,\n    filters=64,\n    rotate_angle=15,\n    epochs=50,\n    batch_size=32,\n    batches_per_epoch=50,\n    val_batches_per_epoch=10,\n    viz_idx=0,\n    reduce_lr_factor=0.1,\n    reduce_lr_patience=3,\n    reduce_lr_min_delta=1e-5,\n    reduce_lr_cooldown=0,\n    reduce_lr_min_lr=1e-10,\n    save_every_epoch=False,\n    amsgrad=False,\n    upsampling_layers=False,\n    ):\n    \"\"\"\n    Trains the network and saves the intermediate results to an output directory.\n\n    :param data_path: Path to an HDF5 file with box and confmaps datasets\n    :param base_output_path: Path to folder in which the run data folder will be saved\n    :param run_name: Name of the training run. If not specified, will be formatted according to other parameters.\n    :param data_name: Name of the dataset for use in formatting run_name\n    :param net_name: Name of the network for use in formatting run_name\n    :param clean: If True, deletes the contents of the run output path\n    :param box_dset: Name of the box dataset in the HDF5 data file\n    :param confmap_dset: Name of the confidence maps dataset in the HDF5 data file\n    :param preshuffle: If True, shuffle prior to splitting the dataset, otherwise validation set will be the last frames\n    :param val_size: Fraction of dataset to use as validation\n    :param filters: Number of filters to use as baseline (see create_model)\n    :param rotate_angle: Images will be augmented by rotating by +-rotate_angle\n    :param epochs: Number of epochs to train for\n    :param batch_size: Number of samples per batch\n    :param batches_per_epoch: Number of batches per epoch (validation is evaluated at the end of the epoch)\n    :param val_batches_per_epoch: Number of batches for validation\n    :param viz_idx: Index of the sample image to use for visualization\n    :param reduce_lr_factor: Factor to reduce the learning rate by (see ReduceLROnPlateau)\n    :param reduce_lr_patience: How many epochs to wait before reduction (see ReduceLROnPlateau)\n    :param reduce_lr_min_delta: Minimum change in error required before reducing LR (see ReduceLROnPlateau)\n    :param reduce_lr_cooldown: How many epochs to wait after reduction before LR can be reduced again (see ReduceLROnPlateau)\n    :param reduce_lr_min_lr: Minimum that the LR can be reduced down to (see ReduceLROnPlateau)\n    :param save_every_epoch: Save weights at every epoch. If False, saves only initial, final and best weights.\n    :param amsgrad: Use AMSGrad variant of optimizer. Can help with training accuracy on rare examples (see Reddi et al., 2018)\n    :param upsampling_layers: Use simple bilinear upsampling layers as opposed to learned transposed convolutions\n    \"\"\"\n\n    # Load\n    print(\"data_path:\", data_path)\n    box, confmap = load_dataset(data_path, X_dset=box_dset, Y_dset=confmap_dset)\n    viz_sample = (box[viz_idx], confmap[viz_idx])\n    box, confmap, val_box, val_confmap, train_idx, val_idx = train_val_split(box, confmap, val_size=val_size, shuffle=preshuffle)\n    print(\"box.shape:\", box.shape)\n    print(\"val_box.shape:\", val_box.shape)\n\n    # Pull out metadata\n    img_size = box.shape[1:]\n    num_output_channels = confmap.shape[-1]\n    print(\"img_size:\", img_size)\n    print(\"num_output_channels:\", num_output_channels)\n\n    # Build run name if needed\n    if data_name == None:\n        data_name = os.path.splitext(os.path.basename(data_path))[0]\n    if run_name == None:\n        # Ex: \"WangMice-DiegoCNN_v1.0_filters=64_rot=15_lrfactor=0.1_lrmindelta=1e-05\"\n        # run_name = \"%s-%s_filters=%d_rot=%d_lrfactor=%.1f_lrmindelta=%g\" % (data_name, net_name, filters, rotate_angle, reduce_lr_factor, reduce_lr_min_delta)\n        run_name = \"%s-%s_epochs=%d\" % (data_name, net_name, epochs)\n    print(\"data_name:\", data_name)\n    print(\"run_name:\", run_name)\n\n    # Create network\n    if isinstance(net_name, keras.models.Model):\n        model = net_name\n        net_name = model.name\n    else:\n        model = create_model(net_name, img_size, num_output_channels, filters=filters, amsgrad=amsgrad, upsampling_layers=upsampling_layers, summary=True)\n    if model == None:\n        print(\"Could not find model:\", net_name)\n        return\n\n    # Initialize run directories\n    run_path = create_run_folders(run_name, base_path=base_output_path, clean=clean)\n    savemat(os.path.join(run_path, \"training_info.mat\"),\n            {\"data_path\": data_path, \"val_idx\": val_idx, \"train_idx\": train_idx,\n             \"base_output_path\": base_output_path, \"run_name\": run_name, \"data_name\": data_name,\n             \"net_name\": net_name, \"clean\": clean, \"box_dset\": box_dset, \"confmap_dset\": confmap_dset,\n             \"preshuffle\": preshuffle, \"val_size\": val_size, \"filters\": filters, \"rotate_angle\": rotate_angle,\n             \"epochs\": epochs, \"batch_size\": batch_size, \"batches_per_epoch\": batches_per_epoch,\n             \"val_batches_per_epoch\": val_batches_per_epoch, \"viz_idx\": viz_idx, \"reduce_lr_factor\": reduce_lr_factor,\n             \"reduce_lr_patience\": reduce_lr_patience, \"reduce_lr_min_delta\": reduce_lr_min_delta,\n             \"reduce_lr_cooldown\": reduce_lr_cooldown, \"reduce_lr_min_lr\": reduce_lr_min_lr,\n             \"save_every_epoch\": save_every_epoch, \"amsgrad\": amsgrad, \"upsampling_layers\": upsampling_layers})\n\n    # Save initial network\n    model.save(os.path.join(run_path, \"initial_model.h5\"))\n\n    # Data generators/augmentation\n    input_layers = model.input_names\n    output_layers = model.output_names\n    if len(input_layers) > 1 or len(output_layers) > 1:\n        train_datagen = MultiInputOutputPairedImageAugmenter(input_layers, output_layers, box, confmap, batch_size=batch_size, shuffle=True, theta=(-rotate_angle, rotate_angle))\n        val_datagen = MultiInputOutputPairedImageAugmenter(input_layers, output_layers, val_box, val_confmap, batch_size=batch_size, shuffle=True, theta=(-rotate_angle, rotate_angle))\n    else:\n        train_datagen = PairedImageAugmenter(box, confmap, batch_size=batch_size, shuffle=True, theta=(-rotate_angle, rotate_angle))\n        val_datagen = PairedImageAugmenter(val_box, val_confmap, batch_size=batch_size, shuffle=True, theta=(-rotate_angle, rotate_angle))\n\n    # Initialize training callbacks\n    history_callback = LossHistory(run_path=run_path)\n    reduce_lr_callback = ReduceLROnPlateau(monitor=\"val_loss\", factor=reduce_lr_factor,\n                                          patience=reduce_lr_patience, verbose=1, mode=\"auto\",\n                                          epsilon=reduce_lr_min_delta, cooldown=reduce_lr_cooldown,\n                                          min_lr=reduce_lr_min_lr)\n    if save_every_epoch:\n        checkpointer = ModelCheckpoint(filepath=os.path.join(run_path, \"weights/weights.{epoch:03d}-{val_loss:.9f}.h5\"), verbose=1, save_best_only=False)\n    else:\n        checkpointer = ModelCheckpoint(filepath=os.path.join(run_path, \"best_model.h5\"), verbose=1, save_best_only=True)\n    viz_grid_callback = LambdaCallback(on_epoch_end=lambda epoch, logs: show_confmap_grid(model, *viz_sample, plot=True, save_path=os.path.join(run_path, \"viz_confmaps/confmaps_%03d.png\" % epoch), show_figure=False))\n    viz_pred_callback = LambdaCallback(on_epoch_end=lambda epoch, logs: show_pred(model, *viz_sample, save_path=os.path.join(run_path, \"viz_pred/pred_%03d.png\" % epoch), show_figure=False))\n\n    # Train!\n    epoch0 = 0\n    t0_train = time()\n    training = model.fit_generator(\n            train_datagen,\n            initial_epoch=epoch0,\n            epochs=epochs,\n            verbose=1,\n    #         use_multiprocessing=True,\n    #         workers=8,\n            steps_per_epoch=batches_per_epoch,\n            max_queue_size=512,\n            shuffle=False,\n            validation_data=val_datagen,\n            validation_steps=val_batches_per_epoch,\n            callbacks = [\n                reduce_lr_callback,\n                checkpointer,\n                history_callback,\n                viz_pred_callback,\n                viz_grid_callback\n            ]\n        )\n\n    # Compute total elapsed time for training\n    elapsed_train = time() - t0_train\n    print(\"Total runtime: %.1f mins\" % (elapsed_train / 60))\n\n    # Save final model\n    model.history = history_callback.history\n    model.save(os.path.join(run_path, \"final_model.h5\"))\n\n\n\n\n\nif __name__ == \"__main__\":\n    # Turn interactive plotting off\n    # plt.ioff()\n\n    # Wrapper for running from commandline\n    clize.run(train)\n"
  },
  {
    "path": "leap/utils.py",
    "content": "import os\nimport numpy as np\nimport re\nfrom time import time\nimport h5py\n\ndef versions(list_devices=False):\n    \"\"\" Prints system info and version strings for finicky libraries. \"\"\"\n    import keras\n    import tensorflow as tf\n    import h5py\n    import platform\n    \n    print(\"Platform:\", platform.platform())\n    print(\"h5py:\\n\" + h5py.version.info)\n    # print(\"numpy:\",np.version.full_version) # h5py already reports this\n    print(\"Keras:\", str(keras.__version__))\n    print(\"Tensorflow:\", str(tf.__version__))\n\n    if list_devices:\n        from tensorflow.python.client import device_lib\n        print(\"Devices:\\n\" + str(device_lib.list_local_devices()))\n\n\ndef find_weights(model_path):\n    \"\"\" Returns paths to saved weights in the run's subfolder.  \"\"\"\n    weights_folder = os.path.join(model_path, \"weights\")\n    weights_paths = sorted(os.listdir(weights_folder))\n    weights_paths = [x for x in weights_paths if \"weights\" in x]\n    matches = [re.match(\"weights[.]([0-9]+)-([0-9.]+)[.]h5\", x).groups() for x in weights_paths]\n    epochs = np.array([int(x[0]) for x in matches])\n    val_losses = np.array([np.float(x[1]) for x in matches])\n    \n    weights_paths = [os.path.join(weights_folder, x) for x in weights_paths]\n    return weights_paths, epochs, val_losses\n\n\ndef find_best_weights(model_path):\n    \"\"\" Returns the path to the model weights with the lowest validation loss. \"\"\"\n    weights_paths, epochs, val_losses = find_weights(model_path)\n    if len(val_losses) > 0:\n        idx = np.argmin(val_losses)\n        return weights_paths[idx]\n    else:\n        return None\n\n\ndef load_dataset(data_path, X_dset=\"box\", Y_dset=\"confmaps\", permute=(0,3,2,1)):\n    \"\"\" Loads and normalizes datasets. \"\"\"\n    \n    # Load\n    t0 = time()\n    with h5py.File(data_path,\"r\") as f:\n        X = f[X_dset][:]\n        Y = f[Y_dset][:]\n    print(\"Loaded %d samples [%.1fs]\" % (len(X), time() - t0))\n    \n    # Adjust dimensions\n    t0 = time()\n    X = preprocess(X, permute)\n    Y = preprocess(Y, permute)\n    print(\"Permuted and normalized data. [%.1fs]\" % (time() - t0))\n    \n    return X, Y\n\ndef preprocess(X, permute=(0,3,2,1)):\n    \"\"\" Normalizes input data. \"\"\"\n    \n    # Add singleton dim for single images\n    if X.ndim == 3:\n        X = X[None,...]\n    \n    # Adjust dimensions\n    X = np.transpose(X, permute)\n    \n    # Normalize\n    if X.dtype == \"uint8\":\n        X = X.astype(\"float32\") / 255\n    \n    return X"
  },
  {
    "path": "leap/viz.py",
    "content": "import numpy as np\nimport matplotlib.pyplot as plt\nplt.switch_backend('agg')\n\n\ndef show_pred(net, X, Y, joint_idx=0, alpha_pred=0.7, save_path=None, show_figure=False):\n    \"\"\"\n    Shows a prediction from the model.\n        net: network to use for prediction\n        idx: index into box/confmap to use or tuple of (box, confmap) with a single sample\n        joint_idx: index of confmap channel to overlay\n        alpha_pred: opacity of confmap overlay\n    \"\"\"\n    # Check inputs\n    # if np.isscalar(idx):\n    #     X = box[idx]\n    #     Y = confmap[idx]\n    # if len(idx) == 2:\n    #     X, Y = idx\n    if X.ndim == 2:\n        X = X[None,...,None]\n    if X.ndim == 3:\n        if X.shape[0] == 1: # missing singleton channel\n            X = X[..., None]\n        elif X.shape[-1] == 1 or X.shape[-1] == 3: # missing sample singleton\n            X = X[None,...]\n    if Y.ndim > 3:\n        Y = Y.squeeze(axis=0)\n        \n    # Predict\n    Y2 = net.predict(X)\n    if type(Y2) == list:\n        Y2 = Y2[-1]\n    Y2 = Y2.squeeze(axis=0)\n    X = X.squeeze()\n    \n    # Find peaks\n    pks_pred = []\n    pks_gt = []\n    for i in range(Y.shape[-1]):\n        Yi = Y[...,i]\n        peak_coord = np.unravel_index(np.argmax(Yi), Yi.shape)\n        pks_gt.append(peak_coord)\n        \n        Yi = Y2[...,i]\n        peak_coord = np.unravel_index(np.argmax(Yi), Yi.shape)\n        pks_pred.append(peak_coord)\n    \n    # Show box image\n    plt.figure(figsize=(6,6))\n    plt.imshow(X, cmap=\"gray\")\n    \n    # Normalize channels\n    for i in range(Y2.shape[-1]):\n        Y2[...,i] /= Y2[...,i].max()\n        \n    # Show prediction overlay\n    plt.imshow(Y2[:,:,joint_idx], alpha=alpha_pred)\n    \n    # Plot peak markers\n    for i in range(Y2.shape[-1]):\n        plt.plot(*pks_gt[i][::-1],   'o', markersize=12, mew=3, mec='w', mfc=[0,0,0,0])\n        plt.plot(*pks_gt[i][::-1],   'o', markersize=12, mew=1, mec='g', mfc=[0,0,0,0])\n        \n        plt.plot(*pks_pred[i][::-1], 'x', markersize=12, mew=3, mec='w')\n        plt.plot(*pks_pred[i][::-1], 'x', markersize=12, mew=1, mec='r')\n        \n    plt.xticks([]), plt.yticks([])\n    plt.tight_layout()\n    \n    if save_path is not None:\n        plt.savefig(save_path, bbox_inches='tight', pad_inches=0)\n    if show_figure:\n        plt.show();\n    else:\n        plt.close()\n\ndef gallery(array, ncols=4):\n    \"\"\" Utility function for tiling a set of images into a grid. \"\"\"\n    array = np.transpose(array.squeeze(), (2,0,1))\n    nindex, height, width = array.shape\n    nrows = int(np.ceil(nindex / ncols))\n    if nindex != nrows * ncols:\n        delta = (nrows * ncols) - nindex\n        array = np.concatenate((array, np.zeros((delta, height, width), dtype=array.dtype)), axis=0)\n\n    assert len(array) == nrows * ncols\n    \n    result = (array.reshape(nrows, ncols, height, width)\n              .swapaxes(1,2)\n              .reshape(height*nrows, width*ncols))\n    return result\n\ndef show_confmap_grid(net, X, Y, plot=True, save_path=None, show_figure=False):\n    \"\"\" \n    Shows predictions from the model using every channel in the confmap.\n    \"\"\"\n    if X.ndim == 2:\n        X = X[None,...,None]\n    if X.ndim == 3:\n        if X.shape[0] == 1: # missing singleton channel\n            X = X[..., None]\n        elif X.shape[-1] == 1 or X.shape[-1] == 3: # missing sample singleton\n            X = X[None,...]\n    if Y.ndim > 3:\n        Y = Y.squeeze(axis=0)\n        \n    # Predict\n    Y2 = net.predict(X)\n    if type(Y2) == list:\n        Y2 = Y2[-1]\n    Y2 = Y2.squeeze(axis=0)\n    X = X.squeeze()\n    \n    # Montage\n    all_Y = np.stack((Y,Y2),axis=-1).reshape(Y.shape[:2] + (-1,))\n    preds = gallery(all_Y, ncols=8)\n    \n    if plot or save_path is not None:\n        # Display\n        plt.figure(figsize=(12,12))\n        plt.imshow(preds)\n        plt.xticks([]),plt.yticks([])\n        if save_path is not None:\n            plt.savefig(save_path, bbox_inches='tight', pad_inches=0)\n        if show_figure:\n            plt.show();\n        else:\n            plt.close()\n    else:\n        return preds\n\ndef plot_history(history, save_path=None, show_figure=False):\n    \"\"\" Plots the training history. \"\"\"\n\n    loss = [x[\"loss\"] for x in history]\n    val_loss = [x[\"val_loss\"] for x in history]\n\n    plt.figure(figsize=(8,4))\n    plt.plot(loss)\n    plt.plot(val_loss)\n    plt.semilogy()\n    plt.grid()\n    plt.xlabel(\"Epochs\")\n    plt.ylabel(\"Loss\")\n    plt.legend([\"Training\", \"Validation\"])\n    \n    if save_path is not None:\n        plt.savefig(save_path)\n    if show_figure:\n        plt.show()\n    else:\n        plt.close()\n"
  },
  {
    "path": "readme.md",
    "content": "# SLEAP: Social LEAP Estimates Animal Poses\n![Social LEAP Estimates Animal Poses](https://raw.githubusercontent.com/talmo/leap/master/docs/sleap_movie.gif \"Social LEAP Estimates Animal Poses\")\n\n**Looking to get some SLEAP?**\n\nWe've released the next version of LEAP, with full support for *multi-animal* pose tracking in addition to all of the functionality of the original LEAP.\n\n***Check it out now at [sleap.ai](https://sleap.ai)!***\n\n## Related software\n- [DeepLabCut](https://github.com/AlexEMG/DeepLabCut): The popular deep learning framework for pose estimation. Soon to support full multi-animal pose tracking!\n- [DeepPoseKit](https://github.com/jgraving/deepposekit): Supports multi-animal pose tracking via top-down centroid detection (e.g., from Ctrax, idTracker, Tractor, etc.).\n- [Animal Part Tracker](https://github.com/kristinbranson/APT): Supports multi-animal pose tracking and lots of other functionality!\n\n\n# LEAP (deprecated)\n\n![LEAP Estimates Animal Pose](https://raw.githubusercontent.com/talmo/leap/master/docs/supp_mov1-long_clip.gif \"LEAP Estimates Animal Pose\")\n\n_Full movie: [YouTube](https://youtu.be/ZmLQNbCbstk)_\n\nThis repository contains code for **LEAP** (_**L**EAP **E**stimates **A**nimal **P**ose_), a framework for animal body part position estimation via deep learning.\n\n**Preprint:** [Pereira et al., bioRxiv (2018)](https://doi.org/10.1101/331181)\n\nWe are still working on documentation and preparing parts of the code. See the Features section below for an overview and status of each component.\n\nWe recommend starting with the [Tutorial: Training Leap From Scratch](https://github.com/talmo/leap/wiki/Tutorial:-Training-LEAP-from-scratch).\n\n## Features\n- [ ] Tracking and alignment code\n- [x] Cluster sampling GUI\n- [x] Skeleton creation GUI (`create_skeleton`)\n- [x] GUI for labeling new dataset (`label_joints`)\n- [x] Network training through the labeling GUI\n- [x] MATLAB (`predict_box.m`) and Python (`leap.predict_box`) interfaces for predicting on new data\n- [ ] GUI for predicting on new data\n- [x] Training data + labels for main fly dataset used in analyses\n- [x] Trained network for predicting on main fly dataset\n- [ ] Analysis/figure generation code\n- [ ] Documentation\n- [x] Examples of usage\n\n## Installation\n### Pre-requisites\nAll neural network and GPU functionality is implemented in Python. The library was designed to be easy to use by providing commandline interfaces, but it can also be used programatically if the MATLAB GUIs are not required.\n\nFor the Python environment, we recommend [Anaconda 5.1.0](https://www.anaconda.com/download/) with Python 3.6.4. Note that **Python 2.x is not supported**.\n\nFor GPU support, you'll want to first install the CUDA drivers with CuDNN and then install these packages:\n```bash\npip install -Iv tensorflow-gpu==1.6.0\npip install -Iv keras==2.1.4\n```\nSee the [TensorFlow installation guide](https://www.tensorflow.org/install/) for more info.\n\nIf you don't have a GPU that supports CUDA, install the regular TensorFlow distribution:\n```bash\npip install tensorflow\n```\n\nPlease note that CPU execution will be MUCH slower (10-20x) than on a GPU. Consider investing in a GPU for your machine if you're thinking of using LEAP routinely.\n\n### Automated installation\nTo get started with using LEAP, open up MATLAB and download the repository:\n```matlab\n>> !git clone https://github.com/talmo/leap.git\n```\n\nThen, install the package and add to the MATLAB path:\n```matlab\n>> cd leap\n>> install_leap\n```\n\nThat's it!\n\nTo avoid having to run this function every time you start MATLAB, save the [MATLAB Search Path](https://www.mathworks.com/help/matlab/matlab_env/what-is-the-matlab-search-path.html) after running it (or just add the `leap` subfolder to your permanent path).\n\n### Manual: MATLAB dependencies\nGUIs and analyses are implemented in MATLAB, but is not required for using the neural network functionality implemented in Python.\n\nWe use **MATLAB R2018a** with the following toolboxes: Parallel Computing Toolbox, Statistics and Machine Learning Toolbox, Computer Vision Toolbox, Image Processing Toolbox, Signal Processing Toolbox.\n\nAll MATLAB external toolboxes are included in the `leap/toolbox` subfolder. Just add the `leap` subdirectory to the [MATLAB Search Path](https://www.mathworks.com/help/matlab/matlab_env/what-is-the-matlab-search-path.html) to access all functionality:\n```matlab\naddpath(genpath('leap'))\n```\n\n### Manual: Python dependencies\nThe versions below were used during development of LEAP but other versions will also likely work.\n\nLibraries required are easily installable via the pip package manager:\n```bash\npip install -Iv numpy==1.14.1\npip install -Iv h5py==2.7.1\npip install -Iv clize==4.0.3\n```\n\nYou will also need OpenCV 3 with Python bindings. We recommend using [skvark's excellent precompiled packages](https://github.com/skvark/opencv-python):\n```bash\npip install -Iv opencv-python==3.4.0.12\n```\n\nYou can install this library as a python package by downloading this git repository:\n\n```bash\ngit clone https://github.com/talmo/leap.git\n```\n\nthen typing:\n```bash\npip install -e leap # installs the leap directory using pip\n```\n\nIf you are using [anaconda](https://conda.io/docs/user-guide/getting-started.html)\nto manage different python environments, it is highly recommended that you use this\nmatlab script to manage your python envs within matlab:\n[condalab](https://github.com/wingillis/condalab) (see their readme for how to use it)\n\n## Usage\nRefer to the [Tutorial: Training Leap From Scratch](https://github.com/talmo/leap/wiki/Tutorial:-Training-LEAP-from-scratch).\n\n### Preprocessing\n\n### GUI Workflow\n1. **Cluster sampling**: Call `cluster_sample` from MATLAB commandline to launch GUI.\n2. **Create skeleton**: Call `create_skeleton` from MATLAB commandline to launch GUI.\n3. **Label data and train**: Call `label_joints` from MATLAB commandline to launch GUI.\n4. **Batch estimation**: _Coming soon._\n\n### Programmatic API\nSee `leap/training.py` and `leap/predict_box.py` for more info.\n\n## Contact and more information\nReach out to us via email: Talmo Pereira (`talmo@princeton.edu`)\n"
  },
  {
    "path": "setup.py",
    "content": "from setuptools import setup\n\nsetup(\n    name=\"leap\",\n    version=\"0.0.1\",\n    author=\"Talmo Pereira\",\n    author_email=\"talmo@princeton.edu\",\n    install_requires=[\n        \"numpy>=1.14.1\",\n        \"h5py>=2.7.1\",\n        \"matplotlib\",\n        \"PyQt5\",\n        \"opencv-python>=3.4.0.12\",\n        \"clize>=4.0.3\"\n    ]\n)\n"
  },
  {
    "path": "uninstall_leap.m",
    "content": "function uninstall_leap()\n%UNINSTALL_LEAP Removes LEAP code from the MATLAB path and Python environment.\n% Usage:\n%   uninstall_leap\n% \n% See also: install_leap, test_leap\n\n% Find base repository path (where this file is contained)\nbasePath = fileparts(which('uninstall_leap'));\n\n% Check if Python package is importable\nstatus = system('pip uninstall -y leap');\n\nworks = test_leap();\nif ~works\n    disp('LEAP uninstalled successfully.')\nend\n\n% Remove from MATLAB path\nrmpath(genpath(fullfile(basePath,'leap')))\n\n\nend\n"
  }
]