<NME> InteractiveHopperSettings.m
<BEF> function [handles] = InteractiveHopperSettings(handles,saveOrLoad,varargin)
% INTERACTIVEHOPPERSETTINGS
%   Save or load GUI settings from the InteractiveHopper example

%-----------------------------------------------------------------------%
% The OpenSim API is a toolkit for musculoskeletal modeling and         %
% simulation. See http://opensim.stanford.edu and the NOTICE file       %
% for more information. OpenSim is developed at Stanford University     %
% and supported by the US National Institutes of Health (U54 GM072970,  %
% R24 HD065690) and by DARPA through the Warrior Web program.           %
%                                                                       %
% Copyright (c) 2017 Stanford University and the Authors                %
% Author(s): Nick Bianco                                                %
%                                                                       %
% Licensed under the Apache License, Version 2.0 (the "License");       %
% you may not use this file except in compliance with the License.      %
% You may obtain a copy of the License at                               %
% http://www.apache.org/licenses/LICENSE-2.0.                           %
%                                                                       %
% Unless required by applicable law or agreed to in writing, software   %
% distributed under the License is distributed on an "AS IS" BASIS,     %
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or       %
% implied. See the License for the specific language governing          %
% permissions and limitations under the License.                        %
%-----------------------------------------------------------------------%

% Parse inputs
p = inputParser();

defaultFilename = 'file';
defaultSetDefaults = false;

addOptional(p,'filename',defaultFilename)
addOptional(p,'setDefaults',defaultSetDefaults)

parse(p,varargin{:})

filename = p.Results.filename;
setDefaults = p.Results.setDefaults;

% Prescibe relevant subfields from GUI handles
subfields = {'Value','Enable','String','Min','Max'};

% Save or load current GUI settings
switch saveOrLoad
    case 'save'
        saveHandles(handles,filename,subfields);
    case 'load'
        if setDefaults
            settings = getDefaults(subfields);
        else
            settings = load(filename);
        end
        
        handles = loadHandles(handles,settings,subfields);      
end

% LOADHANDLES
%   Given a MAT file containing settings for the InteractiveHopper GUI,
%   update the GUI handles to reflect the settings in the relevant setting
%   subfields.
function [handles] = loadHandles(handles,settings,subfields)

handleNames = fieldnames(settings);

for i = 1:length(handleNames)
    for j = 1:length(subfields)
        if isfield(settings.(handleNames{i}),subfields{j})
            handles.(handleNames{i}).(subfields{j}) = settings.(handleNames{i}).(subfields{j});           
        end
    end
end

% SAVEHANDLES
%   Given the current set of handles from the InteractiveHopper GUI, save 
%   the settings from each handle's relevant subfields. Settings are saved
%   to a MAT file in the current working directory.
function saveHandles(handles,filename,subfields)

settings = struct();
handleNames = fieldnames(handles);

for i = 1:length(handleNames)
    for j = 1:length(subfields)
        if isprop(handles.(handleNames{i}),subfields{j})
            settings.(handleNames{i}).(subfields{j}) = handles.(handleNames{i}).(subfields{j});
        end      
    end   
end

save(filename,'-struct','settings')

% SETSUBFIELDS
%   Set values of settings subfields based on optional inputs.
function S = setSubfields(S,subfields,field,varargin)

p = inputParser;

for i = 1:length(subfields)
   addOptional(p,subfields{i},NaN) 
end

parse(p,varargin{:});

for i = 1:length(subfields)
    if ~isnan(p.Results.(subfields{i}))
        fieldnames = {field subfields{i}};
        S = setfield(S,fieldnames{:},p.Results.(subfields{i}));
    end  
end

% GETDEFAULTS
%   These settings reflect the default settings that are called by the
%   InteractiveHopper example upon start up. 
function S = getDefaults(subfields)

muscle = InteractiveHopperParameters('defaultMuscle');

muscleFunc = InteractiveHopperParameters(muscle);
[max_isometric_force,optimal_fiber_length,tendon_slack_length,muscle_mass] = muscleFunc();

controls = InteractiveHopperParameters('controls');
[muscleExcitation,muscleExcitationColor,deviceControl,deviceControlColor] = controls();

passiveSlider = InteractiveHopperParameters('passiveSlider');
[passive_slider,passive_min,passive_max] = passiveSlider();

activeSlider = InteractiveHopperParameters('activeSlider');
[active_slider,active_min,active_max] = activeSlider();

S = struct();

S = setSubfields(S,subfields,'active_as_prop_myo'        ,'Value',0,'Enable','off');
S = setSubfields(S,subfields,'active_mass'               ,'Value',0,'Enable','off','String',' ');
S = setSubfields(S,subfields,'active_mass_text'          ,'Value',0,'Enable','off');
S = setSubfields(S,subfields,'active_mass_units'         ,'Value',0,'Enable','off');
S = setSubfields(S,subfields,'active_patella_wrap'       ,'Value',0,'Enable','off');
S = setSubfields(S,subfields,'active_slider'             ,'Value',active_slider,'Enable','off' ...
                                                         ,'Min',active_min,'Max',active_max);
S = setSubfields(S,subfields,'arnold'                    ,'Value',0,'Enable','on');
S = setSubfields(S,subfields,'average_joe'               ,'Value',1,'Enable','on');
S = setSubfields(S,subfields,'clear'                     ,'Value',0,'Enable','on');
S = setSubfields(S,subfields,'deviceControl'             ,'Value',deviceControl);
S = setSubfields(S,subfields,'deviceControlColor'        ,'Value',deviceControlColor);
S = setSubfields(S,subfields,'enable_active'             ,'Value',0,'Enable','off');
S = setSubfields(S,subfields,'enable_passive'            ,'Value',0,'Enable','off');
S = setSubfields(S,subfields,'katie_ledecky'             ,'Value',0,'Enable','on');
S = setSubfields(S,subfields,'load_setup'                ,'Value',0,'Enable','on');
S = setSubfields(S,subfields,'max_isometric_force'       ,'Value',max_isometric_force,'Enable','off' ...
                                                         ,'String',num2str(max_isometric_force));
S = setSubfields(S,subfields,'max_isometric_force_text'  ,'Value',0,'Enable','on');
S = setSubfields(S,subfields,'max_isometric_force_units' ,'Value',0,'Enable','on');
S = setSubfields(S,subfields,'max_jump_best'             ,'Value',0,'Enable','off');
S = setSubfields(S,subfields,'max_jump_best_text'        ,'Value',0,'Enable','on');
S = setSubfields(S,subfields,'max_jump_recent'           ,'Value',0,'Enable','off');
S = setSubfields(S,subfields,'max_jump_recent_text'      ,'Value',0,'Enable','on');
S = setSubfields(S,subfields,'muscle'                    ,'Value',muscle);
S = setSubfields(S,subfields,'muscleExcitation'          ,'Value',muscleExcitation);
S = setSubfields(S,subfields,'muscleExcitationColor'     ,'Value',muscleExcitationColor);
S = setSubfields(S,subfields,'muscle_mass'               ,'Value',muscle_mass,'Enable','off' ...
                                                         ,'String',num2str(muscle_mass));
S = setSubfields(S,subfields,'muscle_mass_text'          ,'Value',0,'Enable','on');
S = setSubfields(S,subfields,'muscle_mass_units'         ,'Value',0,'Enable','on');
S = setSubfields(S,subfields,'new_device_control'        ,'Value',0,'Enable','off');
S = setSubfields(S,subfields,'new_musc_excitation'       ,'Value',0,'Enable','on');
S = setSubfields(S,subfields,'passive_mass'              ,'Enable','off','String',' ');
S = setSubfields(S,subfields,'passive_mass_text'         ,'Value',0,'Enable','off');
S = setSubfields(S,subfields,'passive_mass_units'        ,'Value',0,'Enable','off');
S = setSubfields(S,subfields,'passive_patella_wrap'      ,'Value',0,'Enable','off');
S = setSubfields(S,subfields,'passive_slider'            ,'Value',passive_slider,'Enable','off' ...
                                                         ,'Min',passive_min,'Max',passive_max);
S = setSubfields(S,subfields,'reset'                     ,'Value',0,'Enable','on');
S = setSubfields(S,subfields,'save_setup'                ,'Value',0,'Enable','on');
S = setSubfields(S,subfields,'setup'                     ,'Value',0,'Enable','on');
S = setSubfields(S,subfields,'simulate'                  ,'Value',0,'Enable','on');
S = setSubfields(S,subfields,'stiffness'                 ,'Enable','off','String',' ');
S = setSubfields(S,subfields,'stiffness_text'            ,'Value',0,'Enable','off');
S = setSubfields(S,subfields,'stiffness_units'           ,'Value',0,'Enable','off');
S = setSubfields(S,subfields,'tendon_slack_length'       ,'Value',tendon_slack_length,'Enable','off' ...
                                                         ,'String',num2str(tendon_slack_length));
S = setSubfields(S,subfields,'tendon_slack_length_text'  ,'Value',0,'Enable','on');
S = setSubfields(S,subfields,'tendon_slack_length_units' ,'Value',0,'Enable','on');
S = setSubfields(S,subfields,'optimal_fiber_length'      ,'Value',optimal_fiber_length,'Enable','off' ...
                                                         ,'String',num2str(optimal_fiber_length));
S = setSubfields(S,subfields,'optimal_fiber_length_text' ,'Value',0,'Enable','on');
S = setSubfields(S,subfields,'optimal_fiber_length_units','Value',0,'Enable','on');
S = setSubfields(S,subfields,'tension_or_gain'           ,'Enable','off','String',' ');
S = setSubfields(S,subfields,'tension_or_gain_text'      ,'Value',0,'Enable','off','String','Max Tension');
S = setSubfields(S,subfields,'tension_or_gain_units'     ,'Value',0,'Enable','off','String','N');
S = setSubfields(S,subfields,'visualize'                 ,'Value',0,'Enable','on');
S = setSubfields(S,subfields,'with_device'               ,'Value',0,'Enable','on');
S = setSubfields(S,subfields,'without_device'            ,'Value',1,'Enable','on');

<MSG> Fade out previous excitations/controls; add margin to user control window; clean up GUI

<DFF> @@ -82,10 +82,16 @@ handleNames = fieldnames(handles);
 
 for i = 1:length(handleNames)
     for j = 1:length(subfields)
-        if isprop(handles.(handleNames{i}),subfields{j})
-            settings.(handleNames{i}).(subfields{j}) = handles.(handleNames{i}).(subfields{j});
-        end      
-    end   
+        if isstruct(handles.(handleNames{i}))
+            if isfield(handles.(handleNames{i}),subfields{j})
+                settings.(handleNames{i}).(subfields{j}) = handles.(handleNames{i}).(subfields{j});
+            end
+        else
+            if isprop(handles.(handleNames{i}),subfields{j})
+                settings.(handleNames{i}).(subfields{j}) = handles.(handleNames{i}).(subfields{j});
+            end
+        end            
+    end
 end
 
 save(filename,'-struct','settings')

{'commit': 'diff --git a/Bindings/Java/Matlab/Hopper_Device/InteractiveHopper.fig b/Bindings/Java/Matlab/Hopper_Device/InteractiveHopper.fig\nindex 751a04fd0..29960b72e 100644\nBinary files a/Bindings/Java/Matlab/Hopper_Device/InteractiveHopper.fig and b/Bindings/Java/Matlab/Hopper_Device/InteractiveHopper.fig differ\ndiff --git a/Bindings/Java/Matlab/Hopper_Device/InteractiveHopper.m b/Bindings/Java/Matlab/Hopper_Device/InteractiveHopper.m\nindex fe749b31b..75d2e415d 100644\n--- a/Bindings/Java/Matlab/Hopper_Device/InteractiveHopper.m\n+++ b/Bindings/Java/Matlab/Hopper_Device/InteractiveHopper.m\n@@ -81,12 +81,23 @@ handles.output = hObject;\n \n % Load default settings\n handles = InteractiveHopperSettings(handles,\'load\',\'setDefaults\',true);\n+handles.muscleExcitationDefault.Value = handles.muscleExcitation.Value;\n+handles.deviceControlDefault.Value = handles.deviceControl.Value;\n \n % Label control axes\n axes(handles.control_axes)\n-axis([0 5 0 1])\n-xlabel(\'Jump Time (s)\')\n-ylabel(\'Excitation\')\n+resetControlAxes()\n+\n+% Initialize control axes line handles lists\n+handles.muscleExcitationList.Value = cell(1);\n+handles.deviceControlList.Value = cell(1);\n+\n+% Label results axes\n+axes(handles.results_axes)\n+resetResultsAxes()\n+\n+% Initialize results axes line handles list\n+handles.resultsList.Value = cell(1);\n \n % Initialize run counter\n setField(handles.run_counter,0)\n@@ -109,19 +120,10 @@ handles.setup.String = sprintf(\'setup_%03.0f\',handles.run_counter.Value);\n \n % Muscle\n muscle = handles.muscle.Value;\n-if isfield(handles,\'muscleExcitation\')\n-    muscleExcitation = handles.muscleExcitation.Value;\n-else\n-    muscleExcitation = [0.0 1.99 2.0 3.89 3.9 4.0;\n-                        0.3 0.3  1.0 1.0  0.1 0.1];\n-    len = size(muscleExcitation,2)+1;\n-    muscleExcitation(1,len) = 5;\n-    muscleExcitation(2,len) = muscleExcitation(2,len-1);\n-    \n-    axes(handles.control_axes)\n-    color = handles.muscleExcitationColor.Value;\n-    pts = line(\'Xdata\',NaN,\'Ydata\',NaN,\'marker\',\'o\',\'LineWidth\',1.5,\'Color\',color);\n-    set(pts,\'Xdata\',muscleExcitation(1,:),\'Ydata\',muscleExcitation(2,:))\n+muscleExcitation = handles.muscleExcitation.Value;\n+if isequal(muscleExcitation, handles.muscleExcitationDefault.Value)\n+    line_h = setMuscleExcitation(muscleExcitation, handles);\n+    handles.muscleExcitationList.Value{1} = line_h;\n end\n \n \n@@ -130,62 +132,47 @@ addPassiveDevice = handles.enable_passive.Value;\n passivePatellaWrap = handles.passive_patella_wrap.Value;\n passiveParameter = handles.passive_slider.Value;\n \n-% Active device \n-addActiveDevice = handles.enable_active.Value;\n-activePatellaWrap = handles.active_patella_wrap.Value;\n-activeParameter = handles.active_slider.Value;\n-isActivePropMyo = handles.active_as_prop_myo.Value;\n+% Active device\n+if handles.with_device.Value\n+    addActiveDevice = handles.enable_active.Value;\n+    activePatellaWrap = handles.active_patella_wrap.Value;\n+    activeParameter = handles.active_slider.Value;\n+    isActivePropMyo = handles.active_as_prop_myo.Value;\n+    deviceControl = handles.deviceControl.Value;\n+else\n+    addActiveDevice = false;\n+    activePatellaWrap = false;\n+    activeParameter = 50;\n+    isActivePropMyo = false;\n+    deviceControl = [0.0 2.5 5.0;\n+                     0.0 0.75 0.0];\n+end\n+\n if addActiveDevice\n     if isActivePropMyo\n         deviceControl = muscleExcitation;\n-    else \n-        if isfield(handles,\'deviceControl\')\n-            deviceControl = handles.deviceControl.Value;\n-        else\n-            deviceControl = [0.0 2.5 5.0;\n-                             0.0 0.75 0.0];\n-            handles.deviceControl.Value = deviceControl;\n-            guidata(hObject, handles)\n-            axes(handles.control_axes)\n-            color = handles.deviceControlColor.Value;\n-            pts = line(\'Xdata\',NaN,\'Ydata\',NaN,\'marker\',\'o\',\'LineWidth\',1.5,\'Color\',color);\n-            set(pts,\'Xdata\',deviceControl(1,:),\'Ydata\',deviceControl(2,:))\n+    else\n+        if isequal(deviceControl, handles.deviceControlDefault.Value)\n+            line_h = setDeviceControl(deviceControl, handles);\n+            handles.deviceControlList.Value{1} = line_h;\n         end\n     end\n-else\n-    deviceControl = [0.0 2.5 5.0;\n-                     0.0 0.75 0.0];\n end\n \n % Other GUI, visualization features\n visualize = handles.visualize.Value;\n \n-% Build or modify hopper solution\n-%if ~exist(\'hopper\',\'var\')\n-    hopper = BuildInteractiveHopperSolution(\'muscle\', muscle, ...\n-             \'muscleExcitation\', muscleExcitation, ...\n-             \'addPassiveDevice\', addPassiveDevice, ...\n-             \'passivePatellaWrap\', passivePatellaWrap, ...\n-             \'passiveParameter\', passiveParameter, ...\n-             \'addActiveDevice\', addActiveDevice, ...\n-             \'activePatellaWrap\', activePatellaWrap, ...\n-             \'isActivePropMyo\', isActivePropMyo, ...\n-             \'activeParameter\', activeParameter, ...\n-             \'deviceControl\',deviceControl);\n-\n-% else\n-%    hopper = ModifyInteractiveHopperSolution(\'muscle\', muscle, ...\n-%              \'muscleExcitation\', muscleExcitation, ...\n-%              \'addPassiveDevice\', addPassiveDevice, ...\n-%              \'passivePatellaWrap\', passivePatellaWrap, ...\n-%              \'passiveParameter\', passiveParameter, ...\n-%              \'addActiveDevice\', addActiveDevice, ...\n-%              \'activePatellaWrap\', activePatellaWrap, ...\n-%              \'isActivePropMyo\', isActivePropMyo, ...\n-%              \'activeParameter\', activeParameter, ...\n-%              \'deviceControl\',deviceControl);    \n-% end\n-         \n+hopper = BuildInteractiveHopperSolution(\'muscle\', muscle, ...\n+    \'muscleExcitation\', muscleExcitation, ...\n+    \'addPassiveDevice\', addPassiveDevice, ...\n+    \'passivePatellaWrap\', passivePatellaWrap, ...\n+    \'passiveParameter\', passiveParameter, ...\n+    \'addActiveDevice\', addActiveDevice, ...\n+    \'activePatellaWrap\', activePatellaWrap, ...\n+    \'isActivePropMyo\', isActivePropMyo, ...\n+    \'activeParameter\', activeParameter, ...\n+    \'deviceControl\',deviceControl);\n+     \n % EvaluateHopper\'s second and third args are bools for visualizing and\n % for printing EvaluateHopper info to console\n [peakHeight, heightStruct] = EvaluateHopper(hopper, visualize, false);\n@@ -202,14 +189,22 @@ end\n \n % Plot results\n axes(handles.results_axes)\n-plot(heightStruct.time, heightStruct.height);\n-xlabel(\'time\');\n-ylabel(\'height\');\n+line_h = plot(heightStruct.time, heightStruct.height,\'b-\',\'LineWidth\',2);\n+\n+handles.resultsList.Value = ...\n+    updateList(handles.resultsList.Value,line_h);\n+\n+xlabel(\'Jump Time (s)\');\n+ylabel(\'Height (m)\');\n+\n+% Update handles structure\n+guidata(hObject, handles);\n        \n % WITHOUT_DEVICE - If no device, disable "Choose Assistive Strategy" panel.\n function without_device_Callback(hObject, eventdata, handles)\n \n set(findall(handles.choose_assistive_strategy, \'-property\', \'enable\'), \'enable\', \'off\')\n+hideDeviceControls(hObject,handles)\n \n % WITH_DEVICE - If adding device, enable option to add device.\n function with_device_Callback(hObject, eventdata, handles)\n@@ -217,13 +212,16 @@ function with_device_Callback(hObject, eventdata, handles)\n set(handles.enable_passive,\'Enable\',\'on\')\n if handles.enable_passive.Value\n     set(findall(handles.passive, \'-property\', \'enable\'), \'enable\', \'on\')\n-\n end\n \n set(handles.enable_active,\'Enable\',\'on\')\n if handles.enable_active.Value\n     set(findall(handles.active, \'-property\', \'enable\'), \'enable\', \'on\')\n+end\n \n+% Show device controls if a non-prop-myo active device is enabled\n+if handles.enable_active.Value && ~handles.active_as_prop_myo.Value\n+    showDeviceControls(hObject,handles)\n end\n \n % AVERAGE_JOE - Sets "The Average Joe" muscle.\n@@ -282,7 +280,11 @@ set(findall(handles.interactive_hopper, \'-property\', \'enable\'), \'enable\', \'off\')\n \n axes(handles.control_axes)\n color = handles.muscleExcitationColor.Value;\n-muscleExcitation = getUserControl(color);\n+[muscleExcitation,line_h] = getUserControl(color);\n+\n+handles.muscleExcitationList.Value = ...\n+    updateList(handles.muscleExcitationList.Value,line_h);\n+\n handles.muscleExcitation.Value = muscleExcitation;\n guidata(hObject, handles)\n \n@@ -308,7 +310,11 @@ set(findall(handles.interactive_hopper, \'-property\', \'enable\'), \'enable\', \'off\')\n \n axes(handles.control_axes)\n color = handles.deviceControlColor.Value;\n-deviceControl = getUserControl(color);\n+[deviceControl, line_h] = getUserControl(color);\n+\n+handles.deviceControlList.Value = ...\n+    updateList(handles.deviceControlList.Value,line_h);\n+\n handles.deviceControl.Value = deviceControl;\n guidata(hObject, handles)\n \n@@ -316,19 +322,32 @@ for i = 1:length(reenable)\n     set(reenable{i},\'Enable\',\'on\')\n end\n \n+% FADE - Fade out RGB value to white by given factor\n+function rgbFaded = fade(factor,rgb)\n+    rgbFaded = factor + (1.0-factor)*rgb;\n+\n % CLEAR - Clear results and control axes.\n function clear_Callback(hObject, eventdata, handles)\n \n axes(handles.results_axes)\n ax = gca;\n cla(ax,\'reset\');\n+resetResultsAxes()\n \n axes(handles.control_axes)\n ax = gca;\n cla(ax,\'reset\');\n-axis([0 5 0 1])\n-xlabel(\'Jump Time (s)\')\n-ylabel(\'Activation\')\n+resetControlAxes()\n+\n+handles.muscleExcitation.Value = handles.muscleExcitationDefault.Value;\n+handles.deviceControl.Value = handles.deviceControlDefault.Value;\n+\n+% Reset line handles lists for both axes\n+handles.resultsList.Value = cell(1);\n+handles.muscleExcitationList.Value = cell(1);\n+handles.deviceControlList.Value = cell(1);\n+\n+guidata(hObject, handles)\n \n % ENABLE_PASSIVE - Add a passive device to the hopper.\n function enable_passive_Callback(hObject, eventdata, handles)\n@@ -345,8 +364,6 @@ else\n     setPassive(handles,[])\n end\n \n-function active_patella_wrap_Callback(hObject, eventdata, handles)\n-\n % PASSIVE_SLIDER - Set passive device parameters.\n function passive_slider_Callback(hObject, eventdata, handles)\n \n@@ -399,9 +416,13 @@ if get(hObject,\'Value\')\n     enable(handles,handle_names,true)\n     active_slider = handles.active_slider.Value;\n     setActive(handles,active_slider)\n+    if ~handles.active_as_prop_myo.Value\n+        showDeviceControls(hObject,handles)\n+    end\n else\n     enable(handles,handle_names,false)\n     setActive(handles,[])\n+    hideDeviceControls(hObject,handles)\n end\n \n % ACTIVE_SLIDER - Set active device parameters.\n@@ -437,17 +458,38 @@ if get(hObject,\'Value\')\n     set(handles.tension_or_gain_text,\'String\',\'Gain\')\n     setField(handles.tension_or_gain_units,[])\n     setActive(handles,active_slider)\n+    hideDeviceControls(hObject,handles)\n else\n     enable(handles,handle_names,true)\n     set(handles.tension_or_gain_text,\'String\',\'Max Tension\')\n     set(handles.tension_or_gain_units,\'String\',\'N\')\n     setActive(handles,active_slider)\n+    showDeviceControls(hObject,handles)\n end\n \n % RESET - Load default GUI settings.\n function reset_Callback(hObject, eventdata, handles)\n \n+clear_Callback(hObject, eventdata, handles)\n+\n handles = InteractiveHopperSettings(handles,\'load\',\'setDefaults\',true);\n+handles.muscleExcitationDefault.Value = handles.muscleExcitation.Value;\n+handles.deviceControlDefault.Value = handles.deviceControl.Value;\n+\n+% Reset control axes\n+axes(handles.control_axes)\n+resetControlAxes()\n+\n+% Reset control axes line handles lists\n+handles.muscleExcitationList.Value = cell(1);\n+handles.deviceControlList.Value = cell(1);\n+\n+% Reset results axes\n+axes(handles.results_axes)\n+resetResultsAxes()\n+\n+% Reset results axes line handles list\n+handles.resultsList.Value = cell(1);\n guidata(hObject, handles);\n \n % SAVE_SETUP - Save current GUI configuration.\n@@ -460,34 +502,71 @@ guidata(hObject, handles);\n % LOAD_SETUP - Load previous GUI configuration.\n function load_setup_Callback(hObject, eventdata, handles)\n \n+reset_Callback(hObject, eventdata, handles)\n+\n runCount = handles.run_counter.Value;\n filename = handles.setup.String;\n handles = InteractiveHopperSettings(handles,\'load\',\'filename\',filename);\n setField(handles.run_counter,runCount)\n+\n+[line_h] = setMuscleExcitation(handles.muscleExcitation.Value,handles);\n+handles.muscleExcitationList.Value = ...\n+    updateList(handles.muscleExcitationList.Value,line_h);\n+[line_h] = setDeviceControl(handles.deviceControl.Value,handles);\n+handles.deviceControlList.Value = ...\n+    updateList(handles.deviceControlList.Value,line_h);\n+\n guidata(hObject, handles);\n \n % GETUSERCONTROL - Get user muscle excitation or device control.\n-function [xy] = getUserControl(color)\n+function [xy,line_h] = getUserControl(color)\n \n-w = [0 5 0 1];\n-axis(w), hold on, grid on\n+resetControlAxes()\n+box = [0 5 0 1];\n+border = [-0.5 5.5 -0.1 1.1];\n \n-title(getString(message(\'SPLINES:resources:axesLabel_WhenYouAreDone\')))\n+title(\'Draw curve in box, click outside gridded area when done\')\n pts = line(\'Xdata\',NaN,\'Ydata\',NaN,\'marker\',\'o\',\'LineWidth\',1.5,\'Color\',color);\n pts2 = line(\'Xdata\',NaN,\'Ydata\',NaN,\'LineStyle\',\'--\',\'LineWidth\',1.5,\'Color\',color);\n \n maxpnts = 100; xy = zeros(2,maxpnts);\n-while 1\n-    for j=2:maxpnts\n+j = 2;\n+while true\n+    while true\n         try\n             [x,y] = ginput(1);\n+            title(\'Draw curve in box, click outside gridded area when done\')\n         catch ME\n             disp(\'ERROR: ginput failed\')\n         end\n-        if isempty(x)||x<w(1)||x>w(2)||y<w(3)||y>w(4)\n+        \n+        left = (x < box(1)) && (x > border(1));\n+        right = (x > box(2)) && (x < border(2));\n+        bottom = (y < box(3)) && (y > border(3));\n+        top = (y > box(4)) && (y < border(4));\n+        \n+        if left || (x<=xy(1,j-1)) \n+            title(\'ERROR: excitation/control must be a function of time \')\n+            continue;\n+        end\n+        \n+        if bottom\n+            y = 0;\n+        elseif top\n+            y = 1;\n+        end\n+            \n+        if isempty(x)||x<border(1)||x>border(2)||y<border(3)||y>border(4)\n             xy(1,j) = 5;\n             xy(2,j) = xy(2,j-1);\n             \n+            set(pts,\'Xdata\',xy(1,1:j),\'Ydata\',xy(2,1:j))\n+            delete(pts2)\n+            break;\n+        elseif right\n+            xy(1,j) = 5;\n+            xy(2,j) = y;\n+            \n             set(pts,\'Xdata\',xy(1,1:j),\'Ydata\',xy(2,1:j))\n             delete(pts2)\n             break;\n@@ -499,16 +578,95 @@ while 1\n         else\n             set(pts,\'Xdata\',x,\'Ydata\',y)\n         end\n+        j=j+1;\n     end\n     \n-    if j>1, break, end\n+    if j>1 \n+        break;\n+    end\n     \n end\n title(\' \')\n xy(:,j) = [5;xy(2,j-1)];\n xy(:,j+1:maxpnts)=[];\n+line_h = pts;\n+\n+% UPDATELIST - Update handle list and fade out previous profiles in list\n+function [list] = updateList(list,line_h)\n+\n+if isempty(list{1})\n+    list{1} = line_h;\n+else\n+    list{length(list)+1} = line_h;\n+    for i = 1:length(list)-1\n+        rgb = list{i}.Color;\n+        rgbFaded = fade(0.5,rgb);\n+        list{i}.Color = rgbFaded;\n+    end\n+end\n+\n+% RESETCONTROLAXES\n+function resetControlAxes()\n+hold on\n+grid on\n+axis([-0.5 5.5 -0.1 1.1])\n+xticks(0:5)\n+yticks(0:0.2:1)\n+rectangle(\'Position\',[0 0 5 1],\'LineWidth\',1.5)\n+xlabel(\'Jump Time (s)\')\n+ylabel(\'Excitation\')\n+\n+% RESETCONTROLAXES\n+function resetResultsAxes()\n+hold on\n+grid on\n+axis([0 5 0 2])\n+xticks(0:5)\n+yticks(0:0.2:2)\n+xlabel(\'Jump Time (s)\')\n+ylabel(\'Jump Height (m)\')\n+\n+% SETMUSCLEEXCITATION\n+function [line_h] = setMuscleExcitation(muscleExcitation,handles)\n+len = size(muscleExcitation,2)+1;\n+muscleExcitation(1,len) = 5;\n+muscleExcitation(2,len) = muscleExcitation(2,len-1);\n+\n+axes(handles.control_axes)\n+color = handles.muscleExcitationColor.Value;\n+line_h = line(\'Xdata\',NaN,\'Ydata\',NaN,\'marker\',\'o\',\'LineWidth\',1.5,\'Color\',color);\n+set(line_h,\'Xdata\',muscleExcitation(1,:),\'Ydata\',muscleExcitation(2,:))\n+\n+% SETDEVICECONTROL\n+function [line_h] = setDeviceControl(deviceControl,handles)\n+axes(handles.control_axes)\n+color = handles.deviceControlColor.Value;\n+line_h = line(\'Xdata\',NaN,\'Ydata\',NaN,\'marker\',\'o\',\'LineWidth\',1.5,\'Color\',color);\n+set(line_h,\'Xdata\',deviceControl(1,:),\'Ydata\',deviceControl(2,:))\n+\n+% HIDEDEVICECONTROLS\n+function hideDeviceControls(hObject,handles)\n+\n+if ~isempty(handles.deviceControlList.Value{1})\n+    for i = 1:length(handles.deviceControlList.Value)\n+        handles.deviceControlList.Value{i}.Visible = \'off\';\n+    end\n+    guidata(hObject, handles)\n+end\n+\n+% SHOWDEVICECONTROLS\n+function showDeviceControls(hObject,handles)\n+\n+if ~isempty(handles.deviceControlList.Value{1})\n+    for i = 1:length(handles.deviceControlList.Value)\n+        handles.deviceControlList.Value{i}.Visible = \'on\';\n+    end\n+    guidata(hObject, handles)\n+end\n \n % Currently unused callback and create functions - DO NOT DELETE\n+function active_patella_wrap_Callback(hObject, eventdata, handles)\n+\n function max_jump_recent_Callback(hObject, eventdata, handles)\n \n function max_jump_recent_CreateFcn(hObject, eventdata, handles)\ndiff --git a/Bindings/Java/Matlab/Hopper_Device/InteractiveHopperSettings.m b/Bindings/Java/Matlab/Hopper_Device/InteractiveHopperSettings.m\nindex a55fabd09..aa8a9d774 100644\n--- a/Bindings/Java/Matlab/Hopper_Device/InteractiveHopperSettings.m\n+++ b/Bindings/Java/Matlab/Hopper_Device/InteractiveHopperSettings.m\n@@ -82,10 +82,16 @@ handleNames = fieldnames(handles);\n \n for i = 1:length(handleNames)\n     for j = 1:length(subfields)\n-        if isprop(handles.(handleNames{i}),subfields{j})\n-            settings.(handleNames{i}).(subfields{j}) = handles.(handleNames{i}).(subfields{j});\n-        end      \n-    end   \n+        if isstruct(handles.(handleNames{i}))\n+            if isfield(handles.(handleNames{i}),subfields{j})\n+                settings.(handleNames{i}).(subfields{j}) = handles.(handleNames{i}).(subfields{j});\n+            end\n+        else\n+            if isprop(handles.(handleNames{i}),subfields{j})\n+                settings.(handleNames{i}).(subfields{j}) = handles.(handleNames{i}).(subfields{j});\n+            end\n+        end            \n+    end\n end\n \n save(filename,\'-struct\',\'settings\')\n', 'commit_subject': 'Fade out previous excitations/controls; add margin to user control window; clean up GUI', 'repo_name': 'opensim-org/opensim-core', 'committer': {'name': 'nickbianco', 'email': '59cf42a68bc0a17efb53743686c585c72fea1db5@stanford.edu', 'time_sec': '1497973770', 'tz_offset': '-420', 'date': {'seconds': '1497973770'}}, 'commit_tree': '1e50a9272f7dcf1a3710d9175ed441f78b14862d', 'commit_message': 'Fade out previous excitations/controls; add margin to user control window; clean up GUI\n', 'language_name': 'Java', 'license': 'apache-2.0', 'hunks': "@@ -82,10 +82,16 @@ handleNames = fieldnames(handles);\n \n for i = 1:length(handleNames)\n     for j = 1:length(subfields)\n-        if isprop(handles.(handleNames{i}),subfields{j})\n-            settings.(handleNames{i}).(subfields{j}) = handles.(handleNames{i}).(subfields{j});\n-        end      \n-    end   \n+        if isstruct(handles.(handleNames{i}))\n+            if isfield(handles.(handleNames{i}),subfields{j})\n+                settings.(handleNames{i}).(subfields{j}) = handles.(handleNames{i}).(subfields{j});\n+            end\n+        else\n+            if isprop(handles.(handleNames{i}),subfields{j})\n+                settings.(handleNames{i}).(subfields{j}) = handles.(handleNames{i}).(subfields{j});\n+            end\n+        end            \n+    end\n end\n \n save(filename,'-struct','settings')\n", 'addition_count': 10, 'deletion_count': 4, 'src_file': 'a/Bindings/Java/Matlab/Hopper_Device/InteractiveHopperSettings.m', 'tgt_file': 'b/Bindings/Java/Matlab/Hopper_Device/InteractiveHopperSettings.m', 'file_extension': '.m', 'before_file': 'function [handles] = InteractiveHopperSettings(handles,saveOrLoad,varargin)\n% INTERACTIVEHOPPERSETTINGS\n%   Save or load GUI settings from the InteractiveHopper example\n\n%-----------------------------------------------------------------------%\n% The OpenSim API is a toolkit for musculoskeletal modeling and         %\n% simulation. See http://opensim.stanford.edu and the NOTICE file       %\n% for more information. OpenSim is developed at Stanford University     %\n% and supported by the US National Institutes of Health (U54 GM072970,  %\n% R24 HD065690) and by DARPA through the Warrior Web program.           %\n%                                                                       %\n% Copyright (c) 2017 Stanford University and the Authors                %\n% Author(s): Nick Bianco                                                %\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% 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       %\n% implied. See the License for the specific language governing          %\n% permissions and limitations under the License.                        %\n%-----------------------------------------------------------------------%\n\n% Parse inputs\np = inputParser();\n\ndefaultFilename = \'file\';\ndefaultSetDefaults = false;\n\naddOptional(p,\'filename\',defaultFilename)\naddOptional(p,\'setDefaults\',defaultSetDefaults)\n\nparse(p,varargin{:})\n\nfilename = p.Results.filename;\nsetDefaults = p.Results.setDefaults;\n\n% Prescibe relevant subfields from GUI handles\nsubfields = {\'Value\',\'Enable\',\'String\',\'Min\',\'Max\'};\n\n% Save or load current GUI settings\nswitch saveOrLoad\n    case \'save\'\n        saveHandles(handles,filename,subfields);\n    case \'load\'\n        if setDefaults\n            settings = getDefaults(subfields);\n        else\n            settings = load(filename);\n        end\n        \n        handles = loadHandles(handles,settings,subfields);      \nend\n\n% LOADHANDLES\n%   Given a MAT file containing settings for the InteractiveHopper GUI,\n%   update the GUI handles to reflect the settings in the relevant setting\n%   subfields.\nfunction [handles] = loadHandles(handles,settings,subfields)\n\nhandleNames = fieldnames(settings);\n\nfor i = 1:length(handleNames)\n    for j = 1:length(subfields)\n        if isfield(settings.(handleNames{i}),subfields{j})\n            handles.(handleNames{i}).(subfields{j}) = settings.(handleNames{i}).(subfields{j});           \n        end\n    end\nend\n\n% SAVEHANDLES\n%   Given the current set of handles from the InteractiveHopper GUI, save \n%   the settings from each handle\'s relevant subfields. Settings are saved\n%   to a MAT file in the current working directory.\nfunction saveHandles(handles,filename,subfields)\n\nsettings = struct();\nhandleNames = fieldnames(handles);\n\nfor i = 1:length(handleNames)\n    for j = 1:length(subfields)\n        if isprop(handles.(handleNames{i}),subfields{j})\n            settings.(handleNames{i}).(subfields{j}) = handles.(handleNames{i}).(subfields{j});\n        end      \n    end   \nend\n\nsave(filename,\'-struct\',\'settings\')\n\n% SETSUBFIELDS\n%   Set values of settings subfields based on optional inputs.\nfunction S = setSubfields(S,subfields,field,varargin)\n\np = inputParser;\n\nfor i = 1:length(subfields)\n   addOptional(p,subfields{i},NaN) \nend\n\nparse(p,varargin{:});\n\nfor i = 1:length(subfields)\n    if ~isnan(p.Results.(subfields{i}))\n        fieldnames = {field subfields{i}};\n        S = setfield(S,fieldnames{:},p.Results.(subfields{i}));\n    end  \nend\n\n% GETDEFAULTS\n%   These settings reflect the default settings that are called by the\n%   InteractiveHopper example upon start up. \nfunction S = getDefaults(subfields)\n\nmuscle = InteractiveHopperParameters(\'defaultMuscle\');\n\nmuscleFunc = InteractiveHopperParameters(muscle);\n[max_isometric_force,optimal_fiber_length,tendon_slack_length,muscle_mass] = muscleFunc();\n\ncontrols = InteractiveHopperParameters(\'controls\');\n[muscleExcitation,muscleExcitationColor,deviceControl,deviceControlColor] = controls();\n\npassiveSlider = InteractiveHopperParameters(\'passiveSlider\');\n[passive_slider,passive_min,passive_max] = passiveSlider();\n\nactiveSlider = InteractiveHopperParameters(\'activeSlider\');\n[active_slider,active_min,active_max] = activeSlider();\n\nS = struct();\n\nS = setSubfields(S,subfields,\'active_as_prop_myo\'        ,\'Value\',0,\'Enable\',\'off\');\nS = setSubfields(S,subfields,\'active_mass\'               ,\'Value\',0,\'Enable\',\'off\',\'String\',\' \');\nS = setSubfields(S,subfields,\'active_mass_text\'          ,\'Value\',0,\'Enable\',\'off\');\nS = setSubfields(S,subfields,\'active_mass_units\'         ,\'Value\',0,\'Enable\',\'off\');\nS = setSubfields(S,subfields,\'active_patella_wrap\'       ,\'Value\',0,\'Enable\',\'off\');\nS = setSubfields(S,subfields,\'active_slider\'             ,\'Value\',active_slider,\'Enable\',\'off\' ...\n                                                         ,\'Min\',active_min,\'Max\',active_max);\nS = setSubfields(S,subfields,\'arnold\'                    ,\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'average_joe\'               ,\'Value\',1,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'clear\'                     ,\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'deviceControl\'             ,\'Value\',deviceControl);\nS = setSubfields(S,subfields,\'deviceControlColor\'        ,\'Value\',deviceControlColor);\nS = setSubfields(S,subfields,\'enable_active\'             ,\'Value\',0,\'Enable\',\'off\');\nS = setSubfields(S,subfields,\'enable_passive\'            ,\'Value\',0,\'Enable\',\'off\');\nS = setSubfields(S,subfields,\'katie_ledecky\'             ,\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'load_setup\'                ,\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'max_isometric_force\'       ,\'Value\',max_isometric_force,\'Enable\',\'off\' ...\n                                                         ,\'String\',num2str(max_isometric_force));\nS = setSubfields(S,subfields,\'max_isometric_force_text\'  ,\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'max_isometric_force_units\' ,\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'max_jump_best\'             ,\'Value\',0,\'Enable\',\'off\');\nS = setSubfields(S,subfields,\'max_jump_best_text\'        ,\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'max_jump_recent\'           ,\'Value\',0,\'Enable\',\'off\');\nS = setSubfields(S,subfields,\'max_jump_recent_text\'      ,\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'muscle\'                    ,\'Value\',muscle);\nS = setSubfields(S,subfields,\'muscleExcitation\'          ,\'Value\',muscleExcitation);\nS = setSubfields(S,subfields,\'muscleExcitationColor\'     ,\'Value\',muscleExcitationColor);\nS = setSubfields(S,subfields,\'muscle_mass\'               ,\'Value\',muscle_mass,\'Enable\',\'off\' ...\n                                                         ,\'String\',num2str(muscle_mass));\nS = setSubfields(S,subfields,\'muscle_mass_text\'          ,\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'muscle_mass_units\'         ,\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'new_device_control\'        ,\'Value\',0,\'Enable\',\'off\');\nS = setSubfields(S,subfields,\'new_musc_excitation\'       ,\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'passive_mass\'              ,\'Enable\',\'off\',\'String\',\' \');\nS = setSubfields(S,subfields,\'passive_mass_text\'         ,\'Value\',0,\'Enable\',\'off\');\nS = setSubfields(S,subfields,\'passive_mass_units\'        ,\'Value\',0,\'Enable\',\'off\');\nS = setSubfields(S,subfields,\'passive_patella_wrap\'      ,\'Value\',0,\'Enable\',\'off\');\nS = setSubfields(S,subfields,\'passive_slider\'            ,\'Value\',passive_slider,\'Enable\',\'off\' ...\n                                                         ,\'Min\',passive_min,\'Max\',passive_max);\nS = setSubfields(S,subfields,\'reset\'                     ,\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'save_setup\'                ,\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'setup\'                     ,\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'simulate\'                  ,\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'stiffness\'                 ,\'Enable\',\'off\',\'String\',\' \');\nS = setSubfields(S,subfields,\'stiffness_text\'            ,\'Value\',0,\'Enable\',\'off\');\nS = setSubfields(S,subfields,\'stiffness_units\'           ,\'Value\',0,\'Enable\',\'off\');\nS = setSubfields(S,subfields,\'tendon_slack_length\'       ,\'Value\',tendon_slack_length,\'Enable\',\'off\' ...\n                                                         ,\'String\',num2str(tendon_slack_length));\nS = setSubfields(S,subfields,\'tendon_slack_length_text\'  ,\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'tendon_slack_length_units\' ,\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'optimal_fiber_length\'      ,\'Value\',optimal_fiber_length,\'Enable\',\'off\' ...\n                                                         ,\'String\',num2str(optimal_fiber_length));\nS = setSubfields(S,subfields,\'optimal_fiber_length_text\' ,\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'optimal_fiber_length_units\',\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'tension_or_gain\'           ,\'Enable\',\'off\',\'String\',\' \');\nS = setSubfields(S,subfields,\'tension_or_gain_text\'      ,\'Value\',0,\'Enable\',\'off\',\'String\',\'Max Tension\');\nS = setSubfields(S,subfields,\'tension_or_gain_units\'     ,\'Value\',0,\'Enable\',\'off\',\'String\',\'N\');\nS = setSubfields(S,subfields,\'visualize\'                 ,\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'with_device\'               ,\'Value\',0,\'Enable\',\'on\');\nS = setSubfields(S,subfields,\'without_device\'            ,\'Value\',1,\'Enable\',\'on\');\n', 'message': 'Fade out previous excitations/controls; add margin to user control window; clean up GUI\n', 'author_name': 'nickbianco', 'lang': 'm'}