From 8de8f49083bca90cc2c34890d89a4b3f55bcda9b Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Thu, 29 Jun 2023 10:57:50 -0500 Subject: [PATCH 01/10] Rename "smoothing" to "window" in EMG Settings #1158 --- OpenBCI_GUI/EmgSettings.pde | 4 ++-- OpenBCI_GUI/EmgSettingsEnums.pde | 4 ++-- OpenBCI_GUI/EmgSettingsUI.pde | 27 +++++++++++++-------------- OpenBCI_GUI/EmgSettingsValues.pde | 8 ++++---- 4 files changed, 21 insertions(+), 22 deletions(-) diff --git a/OpenBCI_GUI/EmgSettings.pde b/OpenBCI_GUI/EmgSettings.pde index 6661b6c4a..e4947932f 100644 --- a/OpenBCI_GUI/EmgSettings.pde +++ b/OpenBCI_GUI/EmgSettings.pde @@ -21,13 +21,13 @@ class EmgSettings { } Gson gson = new Gson(); EmgSettingsValues tempValues = gson.fromJson(fileContents.toString(), EmgSettingsValues.class); - if (tempValues.smoothing.length != channelCount) { + if (tempValues.window.length != channelCount) { outputError("Emg Settings: Loaded EMG Settings file has different number of channels than the current board."); return false; } //Explicitely copy values over to avoid reference issues //(e.g. values = tempValues "nukes" the old values object) - values.smoothing = tempValues.smoothing; + values.window = tempValues.window; values.uvLimit = tempValues.uvLimit; values.creepIncreasing = tempValues.creepIncreasing; values.creepDecreasing = tempValues.creepDecreasing; diff --git a/OpenBCI_GUI/EmgSettingsEnums.pde b/OpenBCI_GUI/EmgSettingsEnums.pde index 819802fc4..c7d195693 100644 --- a/OpenBCI_GUI/EmgSettingsEnums.pde +++ b/OpenBCI_GUI/EmgSettingsEnums.pde @@ -3,7 +3,7 @@ interface EmgSettingsEnum { public String getString(); } -public enum EmgSmoothing implements EmgSettingsEnum +public enum EmgWindow implements EmgSettingsEnum { ONE_HUNDREDTH_SECOND (0, "0.01 s", .01f), ONE_TENTH_SECOND (1, "0.1 s", .1f), @@ -18,7 +18,7 @@ public enum EmgSmoothing implements EmgSettingsEnum private String name; private float value; - EmgSmoothing(int index, String name, float value) { + EmgWindow(int index, String name, float value) { this.index = index; this.name = name; this.value = value; diff --git a/OpenBCI_GUI/EmgSettingsUI.pde b/OpenBCI_GUI/EmgSettingsUI.pde index 5577adec0..2dfdd0aae 100644 --- a/OpenBCI_GUI/EmgSettingsUI.pde +++ b/OpenBCI_GUI/EmgSettingsUI.pde @@ -42,14 +42,14 @@ class EmgSettingsUI extends PApplet implements Runnable { public EmgSettingsValues emgSettingsValues; private TextBox channelColumnLabel; - private TextBox smoothLabel; + private TextBox windowLabel; private TextBox uvLimitLabel; private TextBox creepIncLabel; private TextBox creepDecLabel; private TextBox minDeltaUvLabel; private TextBox lowLimitLabel; - private ScrollableList[] smoothLists; + private ScrollableList[] windowLists; private ScrollableList[] uvLimitLists; private ScrollableList[] creepIncLists; private ScrollableList[] creepDecLists; @@ -129,7 +129,7 @@ class EmgSettingsUI extends PApplet implements Runnable { //Draw column labels channelColumnLabel.draw(); - smoothLabel.draw(); + windowLabel.draw(); uvLimitLabel.draw(); creepIncLabel.draw(); creepDecLabel.draw(); @@ -154,7 +154,7 @@ class EmgSettingsUI extends PApplet implements Runnable { int colOffset = colWidth / 2; int labelY = y + HEADER_HEIGHT / 2; channelColumnLabel.setPosition(x + colOffset, labelY); - smoothLabel.setPosition(x + colOffset + colWidth, labelY); + windowLabel.setPosition(x + colOffset + colWidth, labelY); uvLimitLabel.setPosition(x + colOffset + colWidth*2, labelY); creepIncLabel.setPosition(x + colOffset + colWidth*3, labelY); creepDecLabel.setPosition(x + colOffset + colWidth*4, labelY); @@ -244,8 +244,8 @@ class EmgSettingsUI extends PApplet implements Runnable { dropdownYPositions[i] = HEADER_HEIGHT + int(y + ((ROW_HEIGHT) * i) + (((ROW_HEIGHT) - DROPDOWN_HEIGHT) / 2)); final int buttonXIncrement = DROPDOWN_SPACER + dropdownWidth; - smoothLists[i].setPosition(dropdownX, dropdownYPositions[i]); - smoothLists[i].setSize(dropdownWidth, MAX_HEIGHT_ITEMS * DROPDOWN_HEIGHT); + windowLists[i].setPosition(dropdownX, dropdownYPositions[i]); + windowLists[i].setSize(dropdownWidth, MAX_HEIGHT_ITEMS * DROPDOWN_HEIGHT); dropdownX += buttonXIncrement; uvLimitLists[i].setPosition(dropdownX, dropdownYPositions[i]); @@ -292,7 +292,7 @@ class EmgSettingsUI extends PApplet implements Runnable { int colOffset = colWidth / 2; int labelY = y + HEADER_HEIGHT / 2; channelColumnLabel = new TextBox("Channel", x + colOffset, labelY, labelTxt, labelBG, 12, h3, CENTER, TOP); - smoothLabel = new TextBox("Smooth", x + colOffset + colWidth, labelY, labelTxt, labelBG, 12, h3, CENTER, TOP); + windowLabel = new TextBox("Window", x + colOffset + colWidth, labelY, labelTxt, labelBG, 12, h3, CENTER, TOP); uvLimitLabel = new TextBox("uV Limit", x + colOffset + colWidth*2, labelY, labelTxt, labelBG, 12, h3, CENTER, TOP); creepIncLabel = new TextBox("Creep +", x + colOffset + colWidth*3, labelY, labelTxt, labelBG, 12, h3, CENTER, TOP); creepDecLabel = new TextBox("Creep -", x + colOffset + colWidth*4, labelY, labelTxt, labelBG, 12, h3, CENTER, TOP); @@ -306,7 +306,7 @@ class EmgSettingsUI extends PApplet implements Runnable { //the size and space of these buttons are dependendant on the size of the screen and full ChannelController verbosePrint("EmgChannelSettingsUI: Creating EMG channel setting UI objects..."); - smoothLists = new ScrollableList[channelCount]; + windowLists = new ScrollableList[channelCount]; uvLimitLists = new ScrollableList[channelCount]; creepIncLists = new ScrollableList[channelCount]; creepDecLists = new ScrollableList[channelCount]; @@ -318,7 +318,7 @@ class EmgSettingsUI extends PApplet implements Runnable { //Init dropdowns in reverse so that chan 1 draws on top of chan 2, etc. for (int i = channelCount - 1; i >= 0; i--) { int exgChannel = i; - smoothLists[i] = createDropdown(exgChannel, "smooth_ch_"+(i+1), emgSettingsValues.smoothing[exgChannel].values(), emgSettingsValues.smoothing[exgChannel]); + windowLists[i] = createDropdown(exgChannel, "smooth_ch_"+(i+1), emgSettingsValues.window[exgChannel].values(), emgSettingsValues.window[exgChannel]); uvLimitLists[i] = createDropdown(exgChannel, "uvLimit_ch_"+(i+1), emgSettingsValues.uvLimit[exgChannel].values(), emgSettingsValues.uvLimit[exgChannel]); creepIncLists[i] = createDropdown(exgChannel, "creep_inc_ch_"+(i+1), emgSettingsValues.creepIncreasing[exgChannel].values(), emgSettingsValues.creepIncreasing[exgChannel]); creepDecLists[i] = createDropdown(exgChannel, "creep_dec_ch_"+(i+1), emgSettingsValues.creepDecreasing[exgChannel].values(), emgSettingsValues.creepDecreasing[exgChannel]); @@ -386,9 +386,8 @@ class EmgSettingsUI extends PApplet implements Runnable { EmgSettingsEnum myEnum = (EmgSettingsEnum)bob.get("value"); verbosePrint("EmgSettings: " + (theEvent.getController()).getName() + " == " + myEnum.getString()); - if (myEnum instanceof EmgSmoothing) { - //verbosePrint("HardwareSettings: previousVal == " + emgSettingsValues.previousValues.gain[channel]); - emgSettingsValues.smoothing[channel] = (EmgSmoothing)myEnum; + if (myEnum instanceof EmgWindow) { + emgSettingsValues.window[channel] = (EmgWindow)myEnum; } else if (myEnum instanceof EmgUVLimit) { emgSettingsValues.uvLimit[channel] = (EmgUVLimit)myEnum; } else if (myEnum instanceof EmgCreepIncreasing) { @@ -437,7 +436,7 @@ class EmgSettingsUI extends PApplet implements Runnable { private void updateCp5Objects() { for (int i = 0; i < channelCount; i++) { //Fetch values from the EmgSettingsValues object - EmgSmoothing updateSmoothing = emgSettingsValues.smoothing[i]; + EmgWindow updateSmoothing = emgSettingsValues.window[i]; EmgUVLimit updateUVLimit = emgSettingsValues.uvLimit[i]; EmgCreepIncreasing updateCreepIncreasing = emgSettingsValues.creepIncreasing[i]; EmgCreepDecreasing updateCreepDecreasing = emgSettingsValues.creepDecreasing[i]; @@ -445,7 +444,7 @@ class EmgSettingsUI extends PApplet implements Runnable { EmgLowerThresholdMinimum updateLowerThresholdMinimum = emgSettingsValues.lowerThresholdMinimum[i]; //Update the ScrollableLists - smoothLists[i].getCaptionLabel().setText(updateSmoothing.getString()); + windowLists[i].getCaptionLabel().setText(updateSmoothing.getString()); uvLimitLists[i].getCaptionLabel().setText(updateUVLimit.getString()); creepIncLists[i].getCaptionLabel().setText(updateCreepIncreasing.getString()); creepDecLists[i].getCaptionLabel().setText(updateCreepDecreasing.getString()); diff --git a/OpenBCI_GUI/EmgSettingsValues.pde b/OpenBCI_GUI/EmgSettingsValues.pde index d892b186a..c87d634f8 100644 --- a/OpenBCI_GUI/EmgSettingsValues.pde +++ b/OpenBCI_GUI/EmgSettingsValues.pde @@ -8,7 +8,7 @@ class EmgSettingsValues { //These values can be changed via dropdowns - public EmgSmoothing[] smoothing; + public EmgWindow[] window; public EmgUVLimit[] uvLimit; public EmgCreepIncreasing[] creepIncreasing; public EmgCreepDecreasing[] creepDecreasing; @@ -27,7 +27,7 @@ class EmgSettingsValues { channelCount = currentBoard.getNumEXGChannels(); - smoothing = new EmgSmoothing[channelCount]; + window = new EmgWindow[channelCount]; uvLimit = new EmgUVLimit[channelCount]; creepIncreasing = new EmgCreepIncreasing[channelCount]; creepDecreasing = new EmgCreepDecreasing[channelCount]; @@ -39,7 +39,7 @@ class EmgSettingsValues { lowerThreshold = new float[channelCount]; averageuV = new float[channelCount]; - Arrays.fill(smoothing, EmgSmoothing.ONE_SECOND); + Arrays.fill(window, EmgWindow.ONE_SECOND); Arrays.fill(uvLimit, EmgUVLimit.TWO_HUNDRED_UV); Arrays.fill(creepIncreasing, EmgCreepIncreasing.POINT_9); Arrays.fill(creepDecreasing, EmgCreepDecreasing.POINT_99999); @@ -55,7 +55,7 @@ class EmgSettingsValues { public void process(float[][] data_forDisplay_uV) { //looping over channels and analyzing input data for (int i = 0; i < channelCount; i++) { - float averagePeriod = currentBoard.getSampleRate() * smoothing[i].getValue(); + float averagePeriod = currentBoard.getSampleRate() * window[i].getValue(); int _uvLimit = uvLimit[i].getValue(); float creepSpeedIncreasing = creepIncreasing[i].getValue(); float creepSpeedDecreasing = creepDecreasing[i].getValue(); From dbe46f9131dbcf4a498137c8977908aacd404f1c Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Thu, 29 Jun 2023 10:59:05 -0500 Subject: [PATCH 02/10] Remove travis builds for development branch to save credits --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b3444f4f2..928f9e940 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ jobs: branches: only: - master - - development + #- development before_install: - if [ "$TRAVIS_OS_NAME" = osx ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then openssl aes-256-cbc -K $encrypted_2f5d2771e3cb_key -iv $encrypted_2f5d2771e3cb_iv -in release_script/mac_only/Certificates.p12.enc -out release_script/mac_only/Certificates.p12 -d; fi From ecd6985ad6573104f770dc3b8c11cec2c9190b3b Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Thu, 29 Jun 2023 12:03:55 -0500 Subject: [PATCH 03/10] Add EMG joystick to session settings #1159 --- OpenBCI_GUI/SessionSettings.pde | 57 +++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/OpenBCI_GUI/SessionSettings.pde b/OpenBCI_GUI/SessionSettings.pde index 351d92279..fa7a11ca1 100644 --- a/OpenBCI_GUI/SessionSettings.pde +++ b/OpenBCI_GUI/SessionSettings.pde @@ -189,6 +189,12 @@ class SessionSettings { //Serial load variables int nwSerialBaudRateLoad; + //EMG Widget + List loadEmgActiveChannels = new ArrayList(); + + //EMG Joystick Widget + int loadEmgJoystickSmoothing; + //Primary JSON objects for saving and loading data private JSONObject saveSettingsJSONData; private JSONObject loadSettingsJSONData; @@ -204,6 +210,8 @@ class SessionSettings { private final String kJSONKeyWidget = "widget"; private final String kJSONKeyVersion = "version"; private final String kJSONKeySpectrogram = "spectrogram"; + private final String kJSONKeyEmg = "emg"; + private final String kJSONKeyEmgJoystick = "emgJoystick"; //used only in this class to count the number of channels being used while saving/loading, this gets updated in updateToNChan whenever the number of channels being used changes int slnchan; @@ -453,6 +461,24 @@ class SessionSettings { saveSpectrogramSettings.setInt("Spectrogram_LogLin", spectLogLinSave); saveSettingsJSONData.setJSONObject(kJSONKeySpectrogram, saveSpectrogramSettings); + ///////////////////////////////////////////////Setup new JSON object to save EMG Settings + JSONObject saveEMGSettings = new JSONObject(); + + //Save data from the Active channel checkBoxes + JSONArray saveActiveChanEMG = new JSONArray(); + int numActiveEMGChan = w_emg.emgChannelSelect.activeChan.size(); + for (int i = 0; i < numActiveEMGChan; i++) { + int activeChan = w_emg.emgChannelSelect.activeChan.get(i); + saveActiveChanEMG.setInt(i, activeChan); + } + saveEMGSettings.setJSONArray("activeChannels", saveActiveChanEMG); + saveSettingsJSONData.setJSONObject(kJSONKeyEmg, saveEMGSettings); + + ///////////////////////////////////////////////Setup new JSON object to save EMG Joystick Settings + JSONObject saveEmgJoystickSettings = new JSONObject(); + saveEmgJoystickSettings.setInt("smoothing", w_emgJoystick.joystickSmoothing.getIndex()); + saveSettingsJSONData.setJSONObject(kJSONKeyEmgJoystick, saveEmgJoystickSettings); + ///////////////////////////////////////////////Setup new JSON object to save Widgets Active in respective Containers JSONObject saveWidgetSettings = new JSONObject(); @@ -625,6 +651,18 @@ class SessionSettings { e.printStackTrace(); } + //Get EMG widget settings + loadEmgActiveChannels.clear(); + JSONObject loadEmgSettings = loadSettingsJSONData.getJSONObject(kJSONKeyEmg); + JSONArray loadEmgChan = loadEmgSettings.getJSONArray("activeChannels"); + for (int i = 0; i < loadEmgChan.size(); i++) { + loadEmgActiveChannels.add(loadEmgChan.getInt(i)); + } + + //Get EMG Joystick widget settings + JSONObject loadEmgJoystickSettings = loadSettingsJSONData.getJSONObject(kJSONKeyEmgJoystick); + loadEmgJoystickSmoothing = loadEmgJoystickSettings.getInt("smoothing"); + //get the Widget/Container settings JSONObject loadWidgetSettings = loadSettingsJSONData.getJSONObject(kJSONKeyWidget); //Apply Layout directly before loading and applying widgets to containers @@ -859,6 +897,25 @@ class SessionSettings { break; }//end switch-case for networking settings for all networking protocols + ////////////////////////////Apply EMG widget settings + try { + //apply channel checkbox settings + w_emg.emgChannelSelect.deactivateAllButtons();; + for (int i = 0; i < loadEmgActiveChannels.size(); i++) { + w_emg.emgChannelSelect.setToggleState(loadEmgActiveChannels.get(i), true); + } + } catch (Exception e) { + println("Settings: Exception caught applying EMG widget settings " + e); + } + verbosePrint("Settings: EMG Widget Active Channels: " + loadEmgActiveChannels); + + ////////////////////////////Apply EMG Joystick settings + w_emgJoystick.setJoystickSmoothing(loadEmgJoystickSmoothing); + println("Settings: EMG Joystick Smoothing: " + loadEmgJoystickSmoothing); + println(EmgJoystickSmoothing.getEnumStringsAsList().get(loadEmgJoystickSmoothing)); + w_emgJoystick.cp5_widget.getController("emgJoystickSmoothingDropdown").getCaptionLabel() + .setText(EmgJoystickSmoothing.getEnumStringsAsList().get(loadEmgJoystickSmoothing)); + //////////////////////////////////////////////////////////// // Apply more loaded widget settings above this line // From 8b64f404c39bfb7eaa90576ce899f804e02c3caa Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Thu, 29 Jun 2023 14:30:48 -0500 Subject: [PATCH 04/10] Fix vertical alignment of pop-up text and buttons #1157 --- OpenBCI_GUI/EmgSettingsUI.pde | 49 ++++++++++------------------------- OpenBCI_GUI/PopupMessage.pde | 2 +- 2 files changed, 15 insertions(+), 36 deletions(-) diff --git a/OpenBCI_GUI/EmgSettingsUI.pde b/OpenBCI_GUI/EmgSettingsUI.pde index 2dfdd0aae..fbc92aa67 100644 --- a/OpenBCI_GUI/EmgSettingsUI.pde +++ b/OpenBCI_GUI/EmgSettingsUI.pde @@ -1,6 +1,7 @@ //////////////////////////////////////////////////////////// -// EmgSettingsUI.pde // +// EmgSettingsUI.pde // // Display the Emg Settings UI as a popup // +// Note: This window is never resized. // // // //////////////////////////////////////////////////////////// @@ -14,7 +15,7 @@ class EmgSettingsUI extends PApplet implements Runnable { private ControlP5 emgCp5; private int x, y, w, h; private final int HEADER_HEIGHT = 55; - private final int FOOTER_PADDING = 80; + private final int FOOTER_PADDING = 90; private final int PADDING_3 = 3; private final int PADDING_12 = 12; private final int NUM_CONTROL_BUTTONS = 3; @@ -29,7 +30,7 @@ class EmgSettingsUI extends PApplet implements Runnable { private final int NUM_FOOTER_OBJECTS = 3; private final int FOOTER_OBJECT_WIDTH = 45; private final int FOOTER_OBJECT_HEIGHT = 26; - private int footerObjY = 0; + private int footerObjY; private int[] footerObjX = new int[NUM_FOOTER_OBJECTS]; private final color HEADER_COLOR = OPENBCI_BLUE; @@ -142,28 +143,6 @@ class EmgSettingsUI extends PApplet implements Runnable { emgCp5.draw(); } - private void screenResized() { - x = 0; - y = 0; - w = width; - h = height; - - emgCp5.setGraphics(ourApplet, 0, 0); - - int colWidth = (width / NUM_COLUMNS); - int colOffset = colWidth / 2; - int labelY = y + HEADER_HEIGHT / 2; - channelColumnLabel.setPosition(x + colOffset, labelY); - windowLabel.setPosition(x + colOffset + colWidth, labelY); - uvLimitLabel.setPosition(x + colOffset + colWidth*2, labelY); - creepIncLabel.setPosition(x + colOffset + colWidth*3, labelY); - creepDecLabel.setPosition(x + colOffset + colWidth*4, labelY); - minDeltaUvLabel.setPosition(x + colOffset + colWidth*5, labelY); - lowLimitLabel.setPosition(x + colOffset + colWidth*6, labelY); - - resizeDropdowns(); - } - private void scene() { // Draw background background(BACKGROUND_COLOR); @@ -229,7 +208,7 @@ class EmgSettingsUI extends PApplet implements Runnable { for (int i = 0; i < channelCount; i++) { String channelLabel = channelCount > channelLabels.length ? "Channel " + Integer.toString(i + 1) : channelLabels[i]; - text(channelLabel, x + colOffset, dropdownYPositions[i] + (DROPDOWN_HEIGHT / 2)); + text(channelLabel, x + colOffset, dropdownYPositions[i] + (DROPDOWN_HEIGHT / 2) - 2); } popStyle(); @@ -237,7 +216,7 @@ class EmgSettingsUI extends PApplet implements Runnable { private void resizeDropdowns() { dropdownWidth = int((w - (DROPDOWN_SPACER * (NUM_COLUMNS + 1))) / NUM_COLUMNS); - final int MAX_HEIGHT_ITEMS = channelCount == 4 ? 8 : 5; + final int MAX_HEIGHT_ITEMS = 6; for (int i = 0; i < channelCount; i++) { int dropdownX = x + DROPDOWN_SPACER * 2 + dropdownWidth; @@ -270,7 +249,7 @@ class EmgSettingsUI extends PApplet implements Runnable { } private void createAllUIObjects() { - footerObjY = y + h - FOOTER_PADDING + PADDING_3; + footerObjY = y + h - FOOTER_PADDING/2 - FOOTER_OBJECT_HEIGHT/2; int middle = x + w / 2; int halfObjWidth = FOOTER_OBJECT_WIDTH / 2; footerObjX[0] = middle - halfObjWidth - PADDING_12 - FOOTER_OBJECT_WIDTH; @@ -291,13 +270,13 @@ class EmgSettingsUI extends PApplet implements Runnable { int colWidth = (w / NUM_COLUMNS); int colOffset = colWidth / 2; int labelY = y + HEADER_HEIGHT / 2; - channelColumnLabel = new TextBox("Channel", x + colOffset, labelY, labelTxt, labelBG, 12, h3, CENTER, TOP); - windowLabel = new TextBox("Window", x + colOffset + colWidth, labelY, labelTxt, labelBG, 12, h3, CENTER, TOP); - uvLimitLabel = new TextBox("uV Limit", x + colOffset + colWidth*2, labelY, labelTxt, labelBG, 12, h3, CENTER, TOP); - creepIncLabel = new TextBox("Creep +", x + colOffset + colWidth*3, labelY, labelTxt, labelBG, 12, h3, CENTER, TOP); - creepDecLabel = new TextBox("Creep -", x + colOffset + colWidth*4, labelY, labelTxt, labelBG, 12, h3, CENTER, TOP); - minDeltaUvLabel = new TextBox("Min \u0394uV", x + colOffset + colWidth*5, labelY, labelTxt, labelBG, 12, h3, CENTER, TOP); - lowLimitLabel = new TextBox("Low Limit", x + colOffset + colWidth*6, labelY, labelTxt, labelBG, 12, h3, CENTER, TOP); + channelColumnLabel = new TextBox("Channel", x + colOffset, labelY, labelTxt, labelBG, 12, h3, CENTER, CENTER); + windowLabel = new TextBox("Window", x + colOffset + colWidth, labelY, labelTxt, labelBG, 12, h3, CENTER, CENTER); + uvLimitLabel = new TextBox("uV Limit", x + colOffset + colWidth*2, labelY, labelTxt, labelBG, 12, h3, CENTER, CENTER); + creepIncLabel = new TextBox("Creep +", x + colOffset + colWidth*3, labelY, labelTxt, labelBG, 12, h3, CENTER, CENTER); + creepDecLabel = new TextBox("Creep -", x + colOffset + colWidth*4, labelY, labelTxt, labelBG, 12, h3, CENTER, CENTER); + minDeltaUvLabel = new TextBox("Min \u0394uV", x + colOffset + colWidth*5, labelY, labelTxt, labelBG, 12, h3, CENTER, CENTER); + lowLimitLabel = new TextBox("Low Limit", x + colOffset + colWidth*6, labelY, labelTxt, labelBG, 12, h3, CENTER, CENTER); createAllDropdowns(); } diff --git a/OpenBCI_GUI/PopupMessage.pde b/OpenBCI_GUI/PopupMessage.pde index 58dbe7a0a..ad62d8dbf 100644 --- a/OpenBCI_GUI/PopupMessage.pde +++ b/OpenBCI_GUI/PopupMessage.pde @@ -106,7 +106,7 @@ class PopupMessage extends PApplet implements Runnable { textFont(p0, 24); fill(WHITE); textAlign(LEFT, CENTER); - text(headerMessage, (width - w)/2 + padding, (height - h)/2, w, headerHeight); + text(headerMessage, (width - w)/2 + padding, headerHeight/2); //draw message textFont(p3, 16); From 2a5173c303b03713aeed00b5c9b621f2a625124f Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Thu, 29 Jun 2023 16:42:11 -0500 Subject: [PATCH 05/10] Map channels to EMG Joystick inputs #1156 --- OpenBCI_GUI/SessionSettings.pde | 2 - OpenBCI_GUI/W_EMGJoystick.pde | 239 ++++++++++++++++++++++++++------ 2 files changed, 200 insertions(+), 41 deletions(-) diff --git a/OpenBCI_GUI/SessionSettings.pde b/OpenBCI_GUI/SessionSettings.pde index fa7a11ca1..e13d09aa9 100644 --- a/OpenBCI_GUI/SessionSettings.pde +++ b/OpenBCI_GUI/SessionSettings.pde @@ -911,8 +911,6 @@ class SessionSettings { ////////////////////////////Apply EMG Joystick settings w_emgJoystick.setJoystickSmoothing(loadEmgJoystickSmoothing); - println("Settings: EMG Joystick Smoothing: " + loadEmgJoystickSmoothing); - println(EmgJoystickSmoothing.getEnumStringsAsList().get(loadEmgJoystickSmoothing)); w_emgJoystick.cp5_widget.getController("emgJoystickSmoothingDropdown").getCaptionLabel() .setText(EmgJoystickSmoothing.getEnumStringsAsList().get(loadEmgJoystickSmoothing)); diff --git a/OpenBCI_GUI/W_EMGJoystick.pde b/OpenBCI_GUI/W_EMGJoystick.pde index 527b9e53b..32785c74b 100644 --- a/OpenBCI_GUI/W_EMGJoystick.pde +++ b/OpenBCI_GUI/W_EMGJoystick.pde @@ -45,18 +45,27 @@ class W_EMGJoystick extends Widget { private float leftPolarX, leftPolarY; //9:00 private final int EMG_PLOT_OFFSET = 40; //Used to arrange EMG displays outside of X/Y graph - private final String CHANNEL_ONE_LABEL_EN = "Channel 1"; - private final String CHANNEL_TWO_LABEL_EN = "Channel 2"; - private final String CHANNEL_THREE_LABEL_EN = "Channel 3"; - private final String CHANNEL_FOUR_LABEL_EN = "Channel 4"; - - private String channelOneLabel; - private String channelTwoLabel; - private String channelThreeLabel; - private String channelFourLabel; + private String[] plotChannelLabels = new String[NUM_EMG_CHANNELS]; EmgJoystickSmoothing joystickSmoothing = EmgJoystickSmoothing.POINT_9; + private int DROPDOWN_HEIGHT = navH - 4; + private int DROPDOWN_WIDTH = 80; + private int DROPDOWN_SPACER = 10; + private int DROPDOWN_LABEL_WIDTH = 24; + + EmgJoystickInput[] emgJoystickInputs = new EmgJoystickInput[NUM_EMG_CHANNELS]; + + ScrollableList xNegativeInputDropdown; + ScrollableList xPositiveInputDropdown; + ScrollableList yPositiveInputDropdown; + ScrollableList yNegativeInputDropdown; + + TextBox xNegativeInputDropdownLabel; + TextBox xPositiveInputDropdownLabel; + TextBox yPositiveInputDropdownLabel; + TextBox yNegativeInputDropdownLabel; + W_EMGJoystick(PApplet _parent){ super(_parent); //calls the parent CONSTRUCTOR method of Widget (DON'T REMOVE) @@ -71,13 +80,18 @@ class W_EMGJoystick extends Widget { emgSettingsValues = dataProcessing.emgSettings.values; - channelOneLabel = CHANNEL_ONE_LABEL_EN; - channelTwoLabel = CHANNEL_TWO_LABEL_EN; - channelThreeLabel = CHANNEL_THREE_LABEL_EN; - channelFourLabel = CHANNEL_FOUR_LABEL_EN; + emgJoystickInputs[0] = EmgJoystickInput.CHANNEL_1; + emgJoystickInputs[1] = EmgJoystickInput.CHANNEL_2; + emgJoystickInputs[2] = EmgJoystickInput.CHANNEL_3; + emgJoystickInputs[3] = EmgJoystickInput.CHANNEL_4; + + for (int i = 0; i < NUM_EMG_CHANNELS; i++) { + plotChannelLabels[i] = Integer.toString(emgJoystickInputs[i].getIndex() + 1); + } addDropdown("emgJoystickSmoothingDropdown", "Smoothing", joystickSmoothing.getEnumStringsAsList(), joystickSmoothing.getIndex()); + createInputDropdowns(); } public void update(){ @@ -91,12 +105,14 @@ class W_EMGJoystick extends Widget { drawJoystickXYGraph(); - drawEmgVisualization(0, leftPolarX, leftPolarY); - drawEmgVisualization(1, rightPolarX, rightPolarY); - drawEmgVisualization(2, topPolarX, topPolarY); - drawEmgVisualization(3, bottomPolarX, bottomPolarY); + drawEmgVisualization(emgJoystickInputs[0].getIndex(), leftPolarX, leftPolarY); + drawEmgVisualization(emgJoystickInputs[1].getIndex(), rightPolarX, rightPolarY); + drawEmgVisualization(emgJoystickInputs[2].getIndex(), topPolarX, topPolarY); + drawEmgVisualization(emgJoystickInputs[3].getIndex(), bottomPolarX, bottomPolarY); - drawChannelLabels(); + //drawChannelLabels(); + + drawInputDropdownLabels(); emgCp5.draw(); } @@ -108,6 +124,7 @@ class W_EMGJoystick extends Widget { emgSettingsButton.setPosition(x0 + 1, y0 + navH + 1); updateJoystickGraphSizeAndPosition(); + updateInputDropdownPositions(); } private void updateJoystickGraphSizeAndPosition() { @@ -183,7 +200,8 @@ class W_EMGJoystick extends Widget { popStyle(); } - + + //This is the core method that updates the joystick input private void updateJoystickInput() { previousJoystickRawX = joystickRawX; previousJoystickRawY = joystickRawY; @@ -193,11 +211,16 @@ class W_EMGJoystick extends Widget { joystickRawY = 0; return; } + + float xNegativeValue = emgSettingsValues.outputNormalized[emgJoystickInputs[0].getIndex()]; + float xPositiveValue = emgSettingsValues.outputNormalized[emgJoystickInputs[1].getIndex()]; + float yPositiveValue = emgSettingsValues.outputNormalized[emgJoystickInputs[2].getIndex()]; + float yNegativeValue = emgSettingsValues.outputNormalized[emgJoystickInputs[3].getIndex()]; - //Here we subtract the values of the left and right channels to get the X axis - joystickRawX = emgSettingsValues.outputNormalized[1] - emgSettingsValues.outputNormalized[0]; - //Here we subtract the values of the top and bottom channels to get the Y axis - joystickRawY = emgSettingsValues.outputNormalized[2] - emgSettingsValues.outputNormalized[3]; + //Here we subtract the value of the right channel from the left channel to get the X axis + joystickRawX = xPositiveValue - xNegativeValue; + //Here we subtract the value of the top channel from the bottom channel to get the Y axis + joystickRawY = yPositiveValue - yNegativeValue; //Map the joystick values to a unit circle float[] unitCircleXY = mapToUnitCircle(joystickRawX, joystickRawY); @@ -266,17 +289,6 @@ class W_EMGJoystick extends Widget { noFill(); rect(barX, barY, BAR_WIDTH, BAR_HEIGHT * -1); - /* - //draw channel number at upper left corner of row/column cell - pushStyle(); - stroke(OPENBCI_DARKBLUE); - fill(OPENBCI_DARKBLUE); - int _chan = index+1; - textFont(p5, 12); - text(_chan + "", 10, 20); - popStyle(); - */ - popStyle(); } @@ -287,11 +299,11 @@ class W_EMGJoystick extends Widget { textFont(p4, 14); textLeading(14); textAlign(CENTER,CENTER); - - text(channelOneLabel, leftPolarX, leftPolarY - BAR_CIRCLE_SPACER * 2); - text(channelTwoLabel, rightPolarX, rightPolarY - BAR_CIRCLE_SPACER *2); - text(channelThreeLabel, topPolarX + BAR_CIRCLE_SPACER * 4, topPolarY); - text(channelFourLabel, bottomPolarX + BAR_CIRCLE_SPACER * 4, bottomPolarY); + + text(plotChannelLabels[0], leftPolarX, leftPolarY - BAR_CIRCLE_SPACER * 2); + text(plotChannelLabels[1], rightPolarX, rightPolarY - BAR_CIRCLE_SPACER *2); + text(plotChannelLabels[2], topPolarX + BAR_CIRCLE_SPACER * 4, topPolarY); + text(plotChannelLabels[3], bottomPolarX + BAR_CIRCLE_SPACER * 4, bottomPolarY); popStyle(); } @@ -314,6 +326,104 @@ class W_EMGJoystick extends Widget { emgSettingsButton.setDescription("Click to open the EMG Settings UI to adjust how this metric is calculated."); } + private ScrollableList createEmgJoystickInputDropdown(String name, EmgJoystickInput joystickInput, int inputNumber) { + ScrollableList list = emgCp5.addScrollableList(name) + .setOpen(false) + .setColorBackground(WHITE) // text field bg color + .setColorValueLabel(OPENBCI_DARKBLUE) // text color + .setColorCaptionLabel(OPENBCI_DARKBLUE) + .setColorForeground(color(125)) // border color when not selected + .setColorActive(BUTTON_PRESSED) // border color when selected + .setOutlineColor(OBJECT_BORDER_GREY) + .setSize(DROPDOWN_WIDTH, DROPDOWN_HEIGHT * 6)//temporary size + .setBarHeight(DROPDOWN_HEIGHT) //height of top/primary bar + .setItemHeight(DROPDOWN_HEIGHT) //height of all item/dropdown bars + .setVisible(true) + ; + // this will store the *actual* enum object inside the dropdown! + for (EmgJoystickInput input : EmgJoystickInput.values()) { + if (input.getIndex() >= currentBoard.getNumEXGChannels()) { + continue; + } + list.addItem(input.getString(), input); + } + //Style the text in the ScrollableList + list.getCaptionLabel() //the caption label is the text object in the primary bar + .toUpperCase(false) //DO NOT AUTOSET TO UPPERCASE!!! + .setText(joystickInput.getString()) + .setFont(h5) + .setSize(12) + .getStyle() //need to grab style before affecting the paddingTop + .setPaddingTop(4) + ; + list.getValueLabel() //the value label is connected to the text objects in the dropdown item bars + .toUpperCase(false) //DO NOT AUTOSET TO UPPERCASE!!! + .setText(joystickInput.getString()) + .setFont(p6) + .setSize(10) //set the font size of the item bars to 14pt + .getStyle() //need to grab style before affecting the paddingTop + .setPaddingTop(3) //4-pixel vertical offset to center text + ; + list.addCallback(new SLCallbackListener(inputNumber)); + return list; + } + + private class SLCallbackListener implements CallbackListener { + private int inputNumber; + + SLCallbackListener(int _i) { + inputNumber = _i; + } + public void controlEvent(CallbackEvent theEvent) { + //Selecting an item from ScrollableList triggers Broadcast + if (theEvent.getAction() == ControlP5.ACTION_BROADCAST) { + int val = (int)(theEvent.getController()).getValue(); + Map bob = ((ScrollableList)theEvent.getController()).getItem(val); + emgJoystickInputs[inputNumber] = (EmgJoystickInput)bob.get("value"); + verbosePrint("EmgJoystickInput: " + (theEvent.getController()).getName() + " == " + emgJoystickInputs[inputNumber].getString()); + + plotChannelLabels[inputNumber] = Integer.toString(emgJoystickInputs[inputNumber].getIndex() + 1); + } + } + } + + private void createInputDropdowns() { + //Create the dropdowns in reverse order so that top dropdown draws over bottom dropdown + yNegativeInputDropdown = createEmgJoystickInputDropdown("yNegativeDropdown", emgJoystickInputs[3], 3); + yPositiveInputDropdown = createEmgJoystickInputDropdown("yPositiveDropdown", emgJoystickInputs[2], 2); + xPositiveInputDropdown = createEmgJoystickInputDropdown("xPositiveDropdown", emgJoystickInputs[1], 1); + xNegativeInputDropdown = createEmgJoystickInputDropdown("xNegativeDropdown", emgJoystickInputs[0], 0); + //Add the dropdowns to the list of cp5 elements to check for mouseover + cp5ElementsToCheck.add(xNegativeInputDropdown); + cp5ElementsToCheck.add(xPositiveInputDropdown); + cp5ElementsToCheck.add(yPositiveInputDropdown); + cp5ElementsToCheck.add(yNegativeInputDropdown); + //Create labels for the dropdowns + color labelBG = color(255,255,255,0); + xNegativeInputDropdownLabel = new TextBox("X-", x, y, OPENBCI_DARKBLUE, WHITE, 12, h3, LEFT, TOP); + xPositiveInputDropdownLabel = new TextBox("X+", x, y, OPENBCI_DARKBLUE, WHITE, 12, h3, LEFT, TOP); + yPositiveInputDropdownLabel = new TextBox("Y+", x, y, OPENBCI_DARKBLUE, WHITE, 12, h3, LEFT, TOP); + yNegativeInputDropdownLabel = new TextBox("Y-", x, y, OPENBCI_DARKBLUE, WHITE, 12, h3, LEFT, TOP); + } + + private void updateInputDropdownPositions(){ + xNegativeInputDropdown.setPosition((int) (x + navH + DROPDOWN_LABEL_WIDTH), (int) (y + navH + 1)); + xPositiveInputDropdown.setPosition((int) (x + navH + DROPDOWN_LABEL_WIDTH), (int) (y + navH + DROPDOWN_SPACER + DROPDOWN_HEIGHT)); + yPositiveInputDropdown.setPosition((int) (x + w - navH - DROPDOWN_WIDTH), (int) (y + navH + 1)); + yNegativeInputDropdown.setPosition((int) (x + w - navH - DROPDOWN_WIDTH), (int) (y + navH + DROPDOWN_SPACER + DROPDOWN_HEIGHT)); + xNegativeInputDropdownLabel.setPosition((int) xNegativeInputDropdown.getPosition()[0] - DROPDOWN_LABEL_WIDTH, (int) xNegativeInputDropdown.getPosition()[1]); + xPositiveInputDropdownLabel.setPosition((int) xPositiveInputDropdown.getPosition()[0] - DROPDOWN_LABEL_WIDTH, (int) xPositiveInputDropdown.getPosition()[1]); + yPositiveInputDropdownLabel.setPosition((int) yPositiveInputDropdown.getPosition()[0] - DROPDOWN_LABEL_WIDTH, (int) yPositiveInputDropdown.getPosition()[1]); + yNegativeInputDropdownLabel.setPosition((int) yNegativeInputDropdown.getPosition()[0] - DROPDOWN_LABEL_WIDTH, (int) yNegativeInputDropdown.getPosition()[1]); + } + + private void drawInputDropdownLabels() { + xNegativeInputDropdownLabel.draw(); + xPositiveInputDropdownLabel.draw(); + yPositiveInputDropdownLabel.draw(); + yNegativeInputDropdownLabel.draw(); + } + }; public void emgJoystickSmoothingDropdown(int n) { @@ -353,6 +463,57 @@ public enum EmgJoystickSmoothing implements IndexingInterface return value; } + private static List getEnumStringsAsList() { + List enumStrings = new ArrayList(); + for (IndexingInterface val : vals) { + enumStrings.add(val.getString()); + } + return enumStrings; + } +} + +public enum EmgJoystickInput implements IndexingInterface +{ + CHANNEL_1 (0, "Channel 1", 0), + CHANNEL_2 (1, "Channel 2", 1), + CHANNEL_3 (2, "Channel 3", 2), + CHANNEL_4 (3, "Channel 4", 3), + CHANNEL_5 (4, "Channel 5", 4), + CHANNEL_6 (5, "Channel 6", 5), + CHANNEL_7 (6, "Channel 7", 6), + CHANNEL_8 (7, "Channel 8", 7), + CHANNEL_9 (8, "Channel 9", 8), + CHANNEL_10 (9, "Channel 10", 9), + CHANNEL_11 (10, "Channel 11", 10), + CHANNEL_12 (11, "Channel 12", 11), + CHANNEL_13 (12, "Channel 13", 12), + CHANNEL_14 (13, "Channel 14", 13), + CHANNEL_15 (14, "Channel 15", 14), + CHANNEL_16 (15, "Channel 16", 15); + + private int index; + private String name; + private int value; + private static EmgJoystickInput[] vals = values(); + + EmgJoystickInput(int index, String name, int value) { + this.index = index; + this.name = name; + this.value = value; + } + + public int getIndex() { + return index; + } + + public String getString() { + return name; + } + + public int getValue() { + return value; + } + private static List getEnumStringsAsList() { List enumStrings = new ArrayList(); for (IndexingInterface val : vals) { From b857f04e9bb5725d100c8ddf158c256c66b046ed Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Mon, 10 Jul 2023 12:48:00 -0500 Subject: [PATCH 06/10] Update front-end appearance of EMG Settings UI --- OpenBCI_GUI/EmgSettingsUI.pde | 36 ++++++++--------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/OpenBCI_GUI/EmgSettingsUI.pde b/OpenBCI_GUI/EmgSettingsUI.pde index fbc92aa67..6bb65c253 100644 --- a/OpenBCI_GUI/EmgSettingsUI.pde +++ b/OpenBCI_GUI/EmgSettingsUI.pde @@ -148,26 +148,6 @@ class EmgSettingsUI extends PApplet implements Runnable { background(BACKGROUND_COLOR); } - @Override - public void keyReleased() { - - } - - @Override - public void keyPressed() { - - } - - @Override - public void mousePressed() { - - } - - @Override - public void mouseReleased() { - - } - @Override public void exit() { dispose(); @@ -249,7 +229,7 @@ class EmgSettingsUI extends PApplet implements Runnable { } private void createAllUIObjects() { - footerObjY = y + h - FOOTER_PADDING/2 - FOOTER_OBJECT_HEIGHT/2; + footerObjY = y + h - FOOTER_PADDING/2 - FOOTER_OBJECT_HEIGHT; int middle = x + w / 2; int halfObjWidth = FOOTER_OBJECT_WIDTH / 2; footerObjX[0] = middle - halfObjWidth - PADDING_12 - FOOTER_OBJECT_WIDTH; @@ -270,13 +250,13 @@ class EmgSettingsUI extends PApplet implements Runnable { int colWidth = (w / NUM_COLUMNS); int colOffset = colWidth / 2; int labelY = y + HEADER_HEIGHT / 2; - channelColumnLabel = new TextBox("Channel", x + colOffset, labelY, labelTxt, labelBG, 12, h3, CENTER, CENTER); - windowLabel = new TextBox("Window", x + colOffset + colWidth, labelY, labelTxt, labelBG, 12, h3, CENTER, CENTER); - uvLimitLabel = new TextBox("uV Limit", x + colOffset + colWidth*2, labelY, labelTxt, labelBG, 12, h3, CENTER, CENTER); - creepIncLabel = new TextBox("Creep +", x + colOffset + colWidth*3, labelY, labelTxt, labelBG, 12, h3, CENTER, CENTER); - creepDecLabel = new TextBox("Creep -", x + colOffset + colWidth*4, labelY, labelTxt, labelBG, 12, h3, CENTER, CENTER); - minDeltaUvLabel = new TextBox("Min \u0394uV", x + colOffset + colWidth*5, labelY, labelTxt, labelBG, 12, h3, CENTER, CENTER); - lowLimitLabel = new TextBox("Low Limit", x + colOffset + colWidth*6, labelY, labelTxt, labelBG, 12, h3, CENTER, CENTER); + channelColumnLabel = new TextBox("Channel", x + colOffset, labelY, labelTxt, labelBG, 14, h4, CENTER, CENTER); + windowLabel = new TextBox("Window", x + colOffset + colWidth, labelY, labelTxt, labelBG, 14, h4, CENTER, CENTER); + uvLimitLabel = new TextBox("uV Limit", x + colOffset + colWidth*2, labelY, labelTxt, labelBG, 14, h4, CENTER, CENTER); + creepIncLabel = new TextBox("Creep +", x + colOffset + colWidth*3, labelY, labelTxt, labelBG, 14, h4, CENTER, CENTER); + creepDecLabel = new TextBox("Creep -", x + colOffset + colWidth*4, labelY, labelTxt, labelBG, 14, h4, CENTER, CENTER); + minDeltaUvLabel = new TextBox("Min \u0394uV", x + colOffset + colWidth*5, labelY, labelTxt, labelBG, 14, h4, CENTER, CENTER); + lowLimitLabel = new TextBox("Low Limit", x + colOffset + colWidth*6, labelY, labelTxt, labelBG, 14, h4, CENTER, CENTER); createAllDropdowns(); } From 676cdcbb54088b7447313820736f724a0272b436 Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Mon, 10 Jul 2023 13:40:31 -0500 Subject: [PATCH 07/10] Add ability to save/load emg joystick inputs --- OpenBCI_GUI/SessionSettings.pde | 23 ++++++++++++++++-- OpenBCI_GUI/W_EMGJoystick.pde | 42 +++++++++++++++++++++++++-------- 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/OpenBCI_GUI/SessionSettings.pde b/OpenBCI_GUI/SessionSettings.pde index e13d09aa9..cbfe143e7 100644 --- a/OpenBCI_GUI/SessionSettings.pde +++ b/OpenBCI_GUI/SessionSettings.pde @@ -68,8 +68,8 @@ class SessionSettings { int fftSmoothingSave; int fftFilterSave; //Analog Read settings - int arVertScaleSave; //updates in VertScale_AR() - int arHorizScaleSave; //updates in Duration_AR() + int arVertScaleSave; + int arHorizScaleSave; //Headplot settings int hpIntensitySave; int hpPolaritySave; @@ -194,6 +194,7 @@ class SessionSettings { //EMG Joystick Widget int loadEmgJoystickSmoothing; + List loadEmgJoystickInputs = new ArrayList(); //Primary JSON objects for saving and loading data private JSONObject saveSettingsJSONData; @@ -477,6 +478,12 @@ class SessionSettings { ///////////////////////////////////////////////Setup new JSON object to save EMG Joystick Settings JSONObject saveEmgJoystickSettings = new JSONObject(); saveEmgJoystickSettings.setInt("smoothing", w_emgJoystick.joystickSmoothing.getIndex()); + JSONArray saveEmgJoystickInputs = new JSONArray(); + int numEmgJoystickInputs = w_emgJoystick.emgJoystickInputs.length; + for (int i = 0; i < numEmgJoystickInputs; i++) { + saveEmgJoystickInputs.setInt(i, w_emgJoystick.emgJoystickInputs[i].getIndex()); + } + saveEmgJoystickSettings.setJSONArray("joystickInputs", saveEmgJoystickInputs); saveSettingsJSONData.setJSONObject(kJSONKeyEmgJoystick, saveEmgJoystickSettings); ///////////////////////////////////////////////Setup new JSON object to save Widgets Active in respective Containers @@ -662,6 +669,11 @@ class SessionSettings { //Get EMG Joystick widget settings JSONObject loadEmgJoystickSettings = loadSettingsJSONData.getJSONObject(kJSONKeyEmgJoystick); loadEmgJoystickSmoothing = loadEmgJoystickSettings.getInt("smoothing"); + loadEmgJoystickInputs.clear(); + JSONArray loadJoystickInputsJson = loadEmgJoystickSettings.getJSONArray("joystickInputs"); + for (int i = 0; i < loadJoystickInputsJson.size(); i++) { + loadEmgJoystickInputs.add(loadJoystickInputsJson.getInt(i)); + } //get the Widget/Container settings JSONObject loadWidgetSettings = loadSettingsJSONData.getJSONObject(kJSONKeyWidget); @@ -913,6 +925,13 @@ class SessionSettings { w_emgJoystick.setJoystickSmoothing(loadEmgJoystickSmoothing); w_emgJoystick.cp5_widget.getController("emgJoystickSmoothingDropdown").getCaptionLabel() .setText(EmgJoystickSmoothing.getEnumStringsAsList().get(loadEmgJoystickSmoothing)); + try { + for (int i = 0; i < loadEmgJoystickInputs.size(); i++) { + w_emgJoystick.updateJoystickInput(i, loadEmgJoystickInputs.get(i)); + } + } catch (Exception e) { + println("Settings: Exception caught applying EMG Joystick settings " + e); + } //////////////////////////////////////////////////////////// // Apply more loaded widget settings above this line // diff --git a/OpenBCI_GUI/W_EMGJoystick.pde b/OpenBCI_GUI/W_EMGJoystick.pde index 32785c74b..ffeadb376 100644 --- a/OpenBCI_GUI/W_EMGJoystick.pde +++ b/OpenBCI_GUI/W_EMGJoystick.pde @@ -47,24 +47,24 @@ class W_EMGJoystick extends Widget { private String[] plotChannelLabels = new String[NUM_EMG_CHANNELS]; - EmgJoystickSmoothing joystickSmoothing = EmgJoystickSmoothing.POINT_9; + public EmgJoystickSmoothing joystickSmoothing = EmgJoystickSmoothing.POINT_9; private int DROPDOWN_HEIGHT = navH - 4; private int DROPDOWN_WIDTH = 80; private int DROPDOWN_SPACER = 10; private int DROPDOWN_LABEL_WIDTH = 24; - EmgJoystickInput[] emgJoystickInputs = new EmgJoystickInput[NUM_EMG_CHANNELS]; + public EmgJoystickInput[] emgJoystickInputs = new EmgJoystickInput[NUM_EMG_CHANNELS]; - ScrollableList xNegativeInputDropdown; - ScrollableList xPositiveInputDropdown; - ScrollableList yPositiveInputDropdown; - ScrollableList yNegativeInputDropdown; + private ScrollableList xNegativeInputDropdown; + private ScrollableList xPositiveInputDropdown; + private ScrollableList yPositiveInputDropdown; + private ScrollableList yNegativeInputDropdown; - TextBox xNegativeInputDropdownLabel; - TextBox xPositiveInputDropdownLabel; - TextBox yPositiveInputDropdownLabel; - TextBox yNegativeInputDropdownLabel; + private TextBox xNegativeInputDropdownLabel; + private TextBox xPositiveInputDropdownLabel; + private TextBox yPositiveInputDropdownLabel; + private TextBox yNegativeInputDropdownLabel; W_EMGJoystick(PApplet _parent){ super(_parent); //calls the parent CONSTRUCTOR method of Widget (DON'T REMOVE) @@ -424,6 +424,28 @@ class W_EMGJoystick extends Widget { yNegativeInputDropdownLabel.draw(); } + public void updateJoystickInput(int inputNumber, Integer value) { + if (value == null) { + return; + } + emgJoystickInputs[inputNumber] = EmgJoystickInput.values()[value]; + String inputName = emgJoystickInputs[inputNumber].getString(); + switch (inputNumber) { + case 0: + xNegativeInputDropdown.getCaptionLabel().setText(inputName); + break; + case 1: + xPositiveInputDropdown.getCaptionLabel().setText(inputName); + break; + case 2: + yPositiveInputDropdown.getCaptionLabel().setText(inputName); + break; + case 3: + yNegativeInputDropdown.getCaptionLabel().setText(inputName); + break; + } + } + }; public void emgJoystickSmoothingDropdown(int n) { From 1bc4747c06099747d82c58b4511d6fdeda5ec3ab Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Mon, 10 Jul 2023 14:46:28 -0500 Subject: [PATCH 08/10] Add arrow labels to Emg Joystick Input dropdowns --- OpenBCI_GUI/W_EMGJoystick.pde | 19 +++++++++++++++---- OpenBCI_GUI/data/DOWN_100x100.png | Bin 0 -> 920 bytes OpenBCI_GUI/data/LEFT_100x100.png | Bin 0 -> 876 bytes OpenBCI_GUI/data/RIGHT_100x100.png | Bin 0 -> 846 bytes OpenBCI_GUI/data/UP_100x100.png | Bin 0 -> 935 bytes 5 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 OpenBCI_GUI/data/DOWN_100x100.png create mode 100644 OpenBCI_GUI/data/LEFT_100x100.png create mode 100644 OpenBCI_GUI/data/RIGHT_100x100.png create mode 100644 OpenBCI_GUI/data/UP_100x100.png diff --git a/OpenBCI_GUI/W_EMGJoystick.pde b/OpenBCI_GUI/W_EMGJoystick.pde index ffeadb376..e825d1be1 100644 --- a/OpenBCI_GUI/W_EMGJoystick.pde +++ b/OpenBCI_GUI/W_EMGJoystick.pde @@ -66,6 +66,11 @@ class W_EMGJoystick extends Widget { private TextBox yPositiveInputDropdownLabel; private TextBox yNegativeInputDropdownLabel; + private PImage xNegativeInputLabelImage = loadImage("LEFT_100x100.png"); + private PImage xPositiveInputLabelImage = loadImage("RIGHT_100x100.png"); + private PImage yPositiveInputLabelImage = loadImage("UP_100x100.png"); + private PImage yNegativeInputLabelImage = loadImage("DOWN_100x100.png"); + W_EMGJoystick(PApplet _parent){ super(_parent); //calls the parent CONSTRUCTOR method of Widget (DON'T REMOVE) @@ -109,8 +114,6 @@ class W_EMGJoystick extends Widget { drawEmgVisualization(emgJoystickInputs[1].getIndex(), rightPolarX, rightPolarY); drawEmgVisualization(emgJoystickInputs[2].getIndex(), topPolarX, topPolarY); drawEmgVisualization(emgJoystickInputs[3].getIndex(), bottomPolarX, bottomPolarY); - - //drawChannelLabels(); drawInputDropdownLabels(); @@ -407,10 +410,11 @@ class W_EMGJoystick extends Widget { } private void updateInputDropdownPositions(){ + final int Y_AXIS_ARROW_LABEL_WIDTH = DROPDOWN_HEIGHT + DROPDOWN_SPACER; xNegativeInputDropdown.setPosition((int) (x + navH + DROPDOWN_LABEL_WIDTH), (int) (y + navH + 1)); xPositiveInputDropdown.setPosition((int) (x + navH + DROPDOWN_LABEL_WIDTH), (int) (y + navH + DROPDOWN_SPACER + DROPDOWN_HEIGHT)); - yPositiveInputDropdown.setPosition((int) (x + w - navH - DROPDOWN_WIDTH), (int) (y + navH + 1)); - yNegativeInputDropdown.setPosition((int) (x + w - navH - DROPDOWN_WIDTH), (int) (y + navH + DROPDOWN_SPACER + DROPDOWN_HEIGHT)); + yPositiveInputDropdown.setPosition((int) (x + w - navH - DROPDOWN_WIDTH - Y_AXIS_ARROW_LABEL_WIDTH), (int) (y + navH + 1)); + yNegativeInputDropdown.setPosition((int) (x + w - navH - DROPDOWN_WIDTH - Y_AXIS_ARROW_LABEL_WIDTH), (int) (y + navH + DROPDOWN_SPACER + DROPDOWN_HEIGHT)); xNegativeInputDropdownLabel.setPosition((int) xNegativeInputDropdown.getPosition()[0] - DROPDOWN_LABEL_WIDTH, (int) xNegativeInputDropdown.getPosition()[1]); xPositiveInputDropdownLabel.setPosition((int) xPositiveInputDropdown.getPosition()[0] - DROPDOWN_LABEL_WIDTH, (int) xPositiveInputDropdown.getPosition()[1]); yPositiveInputDropdownLabel.setPosition((int) yPositiveInputDropdown.getPosition()[0] - DROPDOWN_LABEL_WIDTH, (int) yPositiveInputDropdown.getPosition()[1]); @@ -422,6 +426,13 @@ class W_EMGJoystick extends Widget { xPositiveInputDropdownLabel.draw(); yPositiveInputDropdownLabel.draw(); yNegativeInputDropdownLabel.draw(); + + pushStyle(); + final int X_OFFSET = DROPDOWN_WIDTH + DROPDOWN_SPACER; + image(xNegativeInputLabelImage, xNegativeInputDropdown.getPosition()[0] + X_OFFSET, xNegativeInputDropdown.getPosition()[1] + 2, DROPDOWN_HEIGHT, DROPDOWN_HEIGHT); + image(xPositiveInputLabelImage, xPositiveInputDropdown.getPosition()[0] + X_OFFSET, xPositiveInputDropdown.getPosition()[1] + 2, DROPDOWN_HEIGHT, DROPDOWN_HEIGHT); + image(yPositiveInputLabelImage, yPositiveInputDropdown.getPosition()[0] + X_OFFSET, yPositiveInputDropdown.getPosition()[1] + 2, DROPDOWN_HEIGHT, DROPDOWN_HEIGHT); + image(yNegativeInputLabelImage, yNegativeInputDropdown.getPosition()[0] + X_OFFSET, yNegativeInputDropdown.getPosition()[1] + 2, DROPDOWN_HEIGHT, DROPDOWN_HEIGHT); } public void updateJoystickInput(int inputNumber, Integer value) { diff --git a/OpenBCI_GUI/data/DOWN_100x100.png b/OpenBCI_GUI/data/DOWN_100x100.png new file mode 100644 index 0000000000000000000000000000000000000000..72d8b5de81d87109e7bece843c4cfa047cc33881 GIT binary patch literal 920 zcmeAS@N?(olHy`uVBq!ia0vp^DIm z$rE;R+S#HRcq-LVWs||=l!u!pq$o~5D{}Tw(x#0wQrvY!%(ylxie-oytx;1__UXK` zVB&Ax^7D(!izFYPv3&kL?tJ;jzsz;#)3eU~{x`39)2msx&Lmx5`o44P)n{jn92gi` z1gJ%53O#8&YNyKa>CUq=n^@SzO1UnbQD|Bj>DcDAU83##tt*RO&p*lXlGFL)HRa4d z_vdd)^L{Vc_xYaJ=Bi&ecE|iWHDS)u&2dpLWP;8wn6q{1cJ{-zJg2L6cU!LfKGo0A zBzx|JUn13|MjlzI6+6o#r2<~XUehREl0M<*6}?`K??r{PzD!D-b~iY)C~o`SZsENe zB~$0UfA+NY@4k5lr(H46oVxbAqP1kfMbjHKj!WgQ_8B~h+kXD2^x?Wou5Ga!jGx&& ze1V?#J*^~lz5rQnsCQN zJ0$SUH^1p|s{6JX>upPYt$OJ>i{U*Fli@Mz@>oQxv zwj(7sebVcnKNv2Cxo_BJeEoP%O=HGwX1nY)Urfd4E8J>iaNPEDPG`Qr-c9!60>|Rc zfBwL?h<6>|V|F&b>6^AqR_ZVdd-b8?V$`c!eybF2@f1&fCG~jU&s(l+9^YTd~6$89DmU!M0exM$=pEMsTv@G0B3Q)2zug~rS( z|E0Q*{79ZOD literal 0 HcmV?d00001 diff --git a/OpenBCI_GUI/data/LEFT_100x100.png b/OpenBCI_GUI/data/LEFT_100x100.png new file mode 100644 index 0000000000000000000000000000000000000000..1b6b6131f0c3135d59a50fabc8c389e33b3f488f GIT binary patch literal 876 zcmV-y1C#uTP)6?Z?Uff5eSLQKyjuE83>8QNO7YA z83>8Mlyb-=gcQ#L@gao_gyhIN@iV0igycXku`7cJgycY9ajXg%2&o}M#pNnxAf!f2 z5D#h~10gkFrf8{=41`q4@(v1w2q}PVofHTW61hNlFb5F`$v`5`=O_aqDHtto6DJ7WL&$J(mB2lO z)QiW2?IC22ctg-0LRN|IgzOH|7jAcfskq91z~y! zSu8#iq=%4AIkj35lA%Djm+(A<3=pRY&O^uuah=dSgiIDs2+TvsT=AB$JcO(kKM2Z0 z$PNPX5Yk5+Asi1O;aJNhg7FYCPTV6D46@hpNSt`B~hK~>mgl0lo2*OK9Sgd?V z7+zoM!^9N=@%q^tmh+Djiq{`QrqfMpSsTP}0`e*@h5?38gydCzXT-;_K^2DJ?huw& z3wN-%NMK$=4xvwSn9%&@vgjllPA;xVvfVv|97jp#iT8x)HEi7=o)V9DTu%rI}&#f`l6~j37Qi!eg085T797oy;YOPmu6c))2%eNXZWAL=c}K;iz;ch)84_g7^dpS7Z=De1f$6 z_*r8J;u9pil9>eY2@;xR89{u4lx!i0SCDW(x)Q`ENI2^-uO~=r*JUU{e1eqb^%HUn zqF=tg0A6GmB)pY5c?D^~U+49!2;ygZmh*aow9OIeMi4I-vY6KsnR{On3|UQdv8oJeb4PmuInmVpHEa=*RvdV-|yrOY6RmzdWtC5WHR z1o5*^x)8+68R<Gc=lGL(%riwNJ z5u|MnCmHR^B1jo;r)LqQu>hJg4)Ix5uqM`HP3&u#&E8&*AP;36iL~wlSSj|D=0pYqz2vG?^;sN1;N{UhBAWvmdrNt;}kef2R5@QrGNJC<9V!I=K Y2Z(M&iSOg~a{vGU07*qoM6N<$g2Oe0TL1t6 literal 0 HcmV?d00001 diff --git a/OpenBCI_GUI/data/UP_100x100.png b/OpenBCI_GUI/data/UP_100x100.png new file mode 100644 index 0000000000000000000000000000000000000000..562d46f0f0a6d6ce1559a94c10553bc7ed138f0f GIT binary patch literal 935 zcmeAS@N?(olHy`uVBq!ia0vp^DIm#t?~%sV^Z6+f>$SA4FruKWGFb8~;sIlp;E@w5MT?sP3%YdL@Qy8IQ+ z=L7Yl-u?diqt8(4u50CGcazx)FB5nir~f;2Vcw!nmW|zp9J1OC3riPIWZB4mDU8MW zwSy>&r(AL{r^KzqCYA1s9sArFCPr~i;yLE?MNg^in#+U=!>o&sjSQFQXDB*8Iw;YY z$H(AY-MLICB|$>;g%gj&ty+_53QwD5x&)nfceM$sSh;00N!-%B86&i$q^FcKDdR1R zCU3IfuPjEFTTT-mNnE~=?JZGRoO0h|!XXLM7mcnwZ};X{ab6N%%oXd+=sejsUDvTL zna3?X=*Wd#7YdrLn7=7zaJJSr_egUr_-HM2=mKAw5R3S}TYsibF5fVBCeuOI^Um{S zj$XKy#>66SCAqgi#`#x|7z=L`w@mZ9%7%p}dxPawIP}&C#eU;(emzlIQ$xUbTk#=h z9^bqrrzSXjtLRxMyS*?apEm?knTw|@{CKeWjAxeJhJ6c{#x*`* z_IL1SI&$IOBFVUh6BCSgi5j$hl9|?){0i^$Krewj8+~EyNLbFVdQ&MBb@07plSxBvhE literal 0 HcmV?d00001 From 20b3839fe11d908c84516357254f59fe6bc712dc Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Mon, 10 Jul 2023 14:51:29 -0500 Subject: [PATCH 09/10] Update version to v5.2.1 and update changelog --- CHANGELOG.md | 9 +++++++++ OpenBCI_GUI/Info.plist.tmpl | 4 ++-- OpenBCI_GUI/OpenBCI_GUI.pde | 4 ++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 345ef7351..4883d17e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# v5.2.1 + +### Improvements + +- Add ability to map channels to EMG Joystick Inputs #1156 +- Fix alignment of UI objects in popup windows and EMG settings UI #1157 +- Rename "smoothing" to "window" in EMG settings UI #1158 +- Add EMG Joystick settings to Session Settings #1159 + # v5.2.0 ### Bug Fixes diff --git a/OpenBCI_GUI/Info.plist.tmpl b/OpenBCI_GUI/Info.plist.tmpl index 1b1cd08d8..5bf524091 100644 --- a/OpenBCI_GUI/Info.plist.tmpl +++ b/OpenBCI_GUI/Info.plist.tmpl @@ -23,7 +23,7 @@ CFBundleShortVersionString 5 CFBundleVersion - 5.2.0 + 5.2.1 CFBundleSignature ???? NSHumanReadableCopyright @@ -32,7 +32,7 @@ Copyright © 2023 OpenBCI CFBundleGetInfoString - June 2023 + July 2023 @@jvm_runtime@@ diff --git a/OpenBCI_GUI/OpenBCI_GUI.pde b/OpenBCI_GUI/OpenBCI_GUI.pde index f526b9c8d..8433ca6e1 100644 --- a/OpenBCI_GUI/OpenBCI_GUI.pde +++ b/OpenBCI_GUI/OpenBCI_GUI.pde @@ -62,8 +62,8 @@ import java.util.concurrent.atomic.AtomicBoolean; // Global Variables & Instances //------------------------------------------------------------------------ //Used to check GUI version in TopNav.pde and displayed on the splash screen on startup -String localGUIVersionString = "v5.2.0"; -String localGUIVersionDate = "June 2023"; +String localGUIVersionString = "v5.2.1"; +String localGUIVersionDate = "July 2023"; String guiLatestVersionGithubAPI = "https://api.github.com/repos/OpenBCI/OpenBCI_GUI/releases/latest"; String guiLatestReleaseLocation = "https://github.com/OpenBCI/OpenBCI_GUI/releases/latest"; Boolean guiIsUpToDate; From 768b65bce472271a04b8d4e6e2930c809f955434 Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Tue, 11 Jul 2023 10:29:01 -0500 Subject: [PATCH 10/10] Update position of UI objects in EMG Settings UI footer --- OpenBCI_GUI/EmgSettingsUI.pde | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/OpenBCI_GUI/EmgSettingsUI.pde b/OpenBCI_GUI/EmgSettingsUI.pde index 6bb65c253..7573afc4d 100644 --- a/OpenBCI_GUI/EmgSettingsUI.pde +++ b/OpenBCI_GUI/EmgSettingsUI.pde @@ -119,9 +119,11 @@ class EmgSettingsUI extends PApplet implements Runnable { scene(); // Draw header + pushStyle(); noStroke(); fill(HEADER_COLOR); rect(0, 0, width, HEADER_HEIGHT); + popStyle(); emgSettingsValues = dataProcessing.emgSettings.values; @@ -140,7 +142,12 @@ class EmgSettingsUI extends PApplet implements Runnable { drawChannelLabels(); //Draw cp5 objects on top of everything - emgCp5.draw(); + try { + emgCp5.draw(); + } catch (ConcurrentModificationException e) { + e.printStackTrace(); + outputError("EMG Settings UI: Unable to draw cp5 objects."); + } } private void scene() { @@ -229,7 +236,8 @@ class EmgSettingsUI extends PApplet implements Runnable { } private void createAllUIObjects() { - footerObjY = y + h - FOOTER_PADDING/2 - FOOTER_OBJECT_HEIGHT; + final int HALF_FOOTER_HEIGHT = (FOOTER_PADDING + (DROPDOWN_SPACER * 2)) / 2; + footerObjY = y + h - HALF_FOOTER_HEIGHT - (FOOTER_OBJECT_HEIGHT / 2); int middle = x + w / 2; int halfObjWidth = FOOTER_OBJECT_WIDTH / 2; footerObjX[0] = middle - halfObjWidth - PADDING_12 - FOOTER_OBJECT_WIDTH;