Skip to content

Commit 2a5173c

Browse files
committed
Map channels to EMG Joystick inputs #1156
1 parent 8b64f40 commit 2a5173c

2 files changed

Lines changed: 200 additions & 41 deletions

File tree

OpenBCI_GUI/SessionSettings.pde

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -911,8 +911,6 @@ class SessionSettings {
911911

912912
////////////////////////////Apply EMG Joystick settings
913913
w_emgJoystick.setJoystickSmoothing(loadEmgJoystickSmoothing);
914-
println("Settings: EMG Joystick Smoothing: " + loadEmgJoystickSmoothing);
915-
println(EmgJoystickSmoothing.getEnumStringsAsList().get(loadEmgJoystickSmoothing));
916914
w_emgJoystick.cp5_widget.getController("emgJoystickSmoothingDropdown").getCaptionLabel()
917915
.setText(EmgJoystickSmoothing.getEnumStringsAsList().get(loadEmgJoystickSmoothing));
918916

OpenBCI_GUI/W_EMGJoystick.pde

Lines changed: 200 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,27 @@ class W_EMGJoystick extends Widget {
4545
private float leftPolarX, leftPolarY; //9:00
4646
private final int EMG_PLOT_OFFSET = 40; //Used to arrange EMG displays outside of X/Y graph
4747

48-
private final String CHANNEL_ONE_LABEL_EN = "Channel 1";
49-
private final String CHANNEL_TWO_LABEL_EN = "Channel 2";
50-
private final String CHANNEL_THREE_LABEL_EN = "Channel 3";
51-
private final String CHANNEL_FOUR_LABEL_EN = "Channel 4";
52-
53-
private String channelOneLabel;
54-
private String channelTwoLabel;
55-
private String channelThreeLabel;
56-
private String channelFourLabel;
48+
private String[] plotChannelLabels = new String[NUM_EMG_CHANNELS];
5749

5850
EmgJoystickSmoothing joystickSmoothing = EmgJoystickSmoothing.POINT_9;
5951

52+
private int DROPDOWN_HEIGHT = navH - 4;
53+
private int DROPDOWN_WIDTH = 80;
54+
private int DROPDOWN_SPACER = 10;
55+
private int DROPDOWN_LABEL_WIDTH = 24;
56+
57+
EmgJoystickInput[] emgJoystickInputs = new EmgJoystickInput[NUM_EMG_CHANNELS];
58+
59+
ScrollableList xNegativeInputDropdown;
60+
ScrollableList xPositiveInputDropdown;
61+
ScrollableList yPositiveInputDropdown;
62+
ScrollableList yNegativeInputDropdown;
63+
64+
TextBox xNegativeInputDropdownLabel;
65+
TextBox xPositiveInputDropdownLabel;
66+
TextBox yPositiveInputDropdownLabel;
67+
TextBox yNegativeInputDropdownLabel;
68+
6069
W_EMGJoystick(PApplet _parent){
6170
super(_parent); //calls the parent CONSTRUCTOR method of Widget (DON'T REMOVE)
6271

@@ -71,13 +80,18 @@ class W_EMGJoystick extends Widget {
7180

7281
emgSettingsValues = dataProcessing.emgSettings.values;
7382

74-
channelOneLabel = CHANNEL_ONE_LABEL_EN;
75-
channelTwoLabel = CHANNEL_TWO_LABEL_EN;
76-
channelThreeLabel = CHANNEL_THREE_LABEL_EN;
77-
channelFourLabel = CHANNEL_FOUR_LABEL_EN;
83+
emgJoystickInputs[0] = EmgJoystickInput.CHANNEL_1;
84+
emgJoystickInputs[1] = EmgJoystickInput.CHANNEL_2;
85+
emgJoystickInputs[2] = EmgJoystickInput.CHANNEL_3;
86+
emgJoystickInputs[3] = EmgJoystickInput.CHANNEL_4;
87+
88+
for (int i = 0; i < NUM_EMG_CHANNELS; i++) {
89+
plotChannelLabels[i] = Integer.toString(emgJoystickInputs[i].getIndex() + 1);
90+
}
7891

7992
addDropdown("emgJoystickSmoothingDropdown", "Smoothing", joystickSmoothing.getEnumStringsAsList(), joystickSmoothing.getIndex());
8093

94+
createInputDropdowns();
8195
}
8296

8397
public void update(){
@@ -91,12 +105,14 @@ class W_EMGJoystick extends Widget {
91105

92106
drawJoystickXYGraph();
93107

94-
drawEmgVisualization(0, leftPolarX, leftPolarY);
95-
drawEmgVisualization(1, rightPolarX, rightPolarY);
96-
drawEmgVisualization(2, topPolarX, topPolarY);
97-
drawEmgVisualization(3, bottomPolarX, bottomPolarY);
108+
drawEmgVisualization(emgJoystickInputs[0].getIndex(), leftPolarX, leftPolarY);
109+
drawEmgVisualization(emgJoystickInputs[1].getIndex(), rightPolarX, rightPolarY);
110+
drawEmgVisualization(emgJoystickInputs[2].getIndex(), topPolarX, topPolarY);
111+
drawEmgVisualization(emgJoystickInputs[3].getIndex(), bottomPolarX, bottomPolarY);
98112

99-
drawChannelLabels();
113+
//drawChannelLabels();
114+
115+
drawInputDropdownLabels();
100116

101117
emgCp5.draw();
102118
}
@@ -108,6 +124,7 @@ class W_EMGJoystick extends Widget {
108124
emgSettingsButton.setPosition(x0 + 1, y0 + navH + 1);
109125

110126
updateJoystickGraphSizeAndPosition();
127+
updateInputDropdownPositions();
111128
}
112129

113130
private void updateJoystickGraphSizeAndPosition() {
@@ -183,7 +200,8 @@ class W_EMGJoystick extends Widget {
183200

184201
popStyle();
185202
}
186-
203+
204+
//This is the core method that updates the joystick input
187205
private void updateJoystickInput() {
188206
previousJoystickRawX = joystickRawX;
189207
previousJoystickRawY = joystickRawY;
@@ -193,11 +211,16 @@ class W_EMGJoystick extends Widget {
193211
joystickRawY = 0;
194212
return;
195213
}
214+
215+
float xNegativeValue = emgSettingsValues.outputNormalized[emgJoystickInputs[0].getIndex()];
216+
float xPositiveValue = emgSettingsValues.outputNormalized[emgJoystickInputs[1].getIndex()];
217+
float yPositiveValue = emgSettingsValues.outputNormalized[emgJoystickInputs[2].getIndex()];
218+
float yNegativeValue = emgSettingsValues.outputNormalized[emgJoystickInputs[3].getIndex()];
196219

197-
//Here we subtract the values of the left and right channels to get the X axis
198-
joystickRawX = emgSettingsValues.outputNormalized[1] - emgSettingsValues.outputNormalized[0];
199-
//Here we subtract the values of the top and bottom channels to get the Y axis
200-
joystickRawY = emgSettingsValues.outputNormalized[2] - emgSettingsValues.outputNormalized[3];
220+
//Here we subtract the value of the right channel from the left channel to get the X axis
221+
joystickRawX = xPositiveValue - xNegativeValue;
222+
//Here we subtract the value of the top channel from the bottom channel to get the Y axis
223+
joystickRawY = yPositiveValue - yNegativeValue;
201224

202225
//Map the joystick values to a unit circle
203226
float[] unitCircleXY = mapToUnitCircle(joystickRawX, joystickRawY);
@@ -266,17 +289,6 @@ class W_EMGJoystick extends Widget {
266289
noFill();
267290
rect(barX, barY, BAR_WIDTH, BAR_HEIGHT * -1);
268291

269-
/*
270-
//draw channel number at upper left corner of row/column cell
271-
pushStyle();
272-
stroke(OPENBCI_DARKBLUE);
273-
fill(OPENBCI_DARKBLUE);
274-
int _chan = index+1;
275-
textFont(p5, 12);
276-
text(_chan + "", 10, 20);
277-
popStyle();
278-
*/
279-
280292
popStyle();
281293
}
282294

@@ -287,11 +299,11 @@ class W_EMGJoystick extends Widget {
287299
textFont(p4, 14);
288300
textLeading(14);
289301
textAlign(CENTER,CENTER);
290-
291-
text(channelOneLabel, leftPolarX, leftPolarY - BAR_CIRCLE_SPACER * 2);
292-
text(channelTwoLabel, rightPolarX, rightPolarY - BAR_CIRCLE_SPACER *2);
293-
text(channelThreeLabel, topPolarX + BAR_CIRCLE_SPACER * 4, topPolarY);
294-
text(channelFourLabel, bottomPolarX + BAR_CIRCLE_SPACER * 4, bottomPolarY);
302+
303+
text(plotChannelLabels[0], leftPolarX, leftPolarY - BAR_CIRCLE_SPACER * 2);
304+
text(plotChannelLabels[1], rightPolarX, rightPolarY - BAR_CIRCLE_SPACER *2);
305+
text(plotChannelLabels[2], topPolarX + BAR_CIRCLE_SPACER * 4, topPolarY);
306+
text(plotChannelLabels[3], bottomPolarX + BAR_CIRCLE_SPACER * 4, bottomPolarY);
295307

296308
popStyle();
297309
}
@@ -314,6 +326,104 @@ class W_EMGJoystick extends Widget {
314326
emgSettingsButton.setDescription("Click to open the EMG Settings UI to adjust how this metric is calculated.");
315327
}
316328

329+
private ScrollableList createEmgJoystickInputDropdown(String name, EmgJoystickInput joystickInput, int inputNumber) {
330+
ScrollableList list = emgCp5.addScrollableList(name)
331+
.setOpen(false)
332+
.setColorBackground(WHITE) // text field bg color
333+
.setColorValueLabel(OPENBCI_DARKBLUE) // text color
334+
.setColorCaptionLabel(OPENBCI_DARKBLUE)
335+
.setColorForeground(color(125)) // border color when not selected
336+
.setColorActive(BUTTON_PRESSED) // border color when selected
337+
.setOutlineColor(OBJECT_BORDER_GREY)
338+
.setSize(DROPDOWN_WIDTH, DROPDOWN_HEIGHT * 6)//temporary size
339+
.setBarHeight(DROPDOWN_HEIGHT) //height of top/primary bar
340+
.setItemHeight(DROPDOWN_HEIGHT) //height of all item/dropdown bars
341+
.setVisible(true)
342+
;
343+
// this will store the *actual* enum object inside the dropdown!
344+
for (EmgJoystickInput input : EmgJoystickInput.values()) {
345+
if (input.getIndex() >= currentBoard.getNumEXGChannels()) {
346+
continue;
347+
}
348+
list.addItem(input.getString(), input);
349+
}
350+
//Style the text in the ScrollableList
351+
list.getCaptionLabel() //the caption label is the text object in the primary bar
352+
.toUpperCase(false) //DO NOT AUTOSET TO UPPERCASE!!!
353+
.setText(joystickInput.getString())
354+
.setFont(h5)
355+
.setSize(12)
356+
.getStyle() //need to grab style before affecting the paddingTop
357+
.setPaddingTop(4)
358+
;
359+
list.getValueLabel() //the value label is connected to the text objects in the dropdown item bars
360+
.toUpperCase(false) //DO NOT AUTOSET TO UPPERCASE!!!
361+
.setText(joystickInput.getString())
362+
.setFont(p6)
363+
.setSize(10) //set the font size of the item bars to 14pt
364+
.getStyle() //need to grab style before affecting the paddingTop
365+
.setPaddingTop(3) //4-pixel vertical offset to center text
366+
;
367+
list.addCallback(new SLCallbackListener(inputNumber));
368+
return list;
369+
}
370+
371+
private class SLCallbackListener implements CallbackListener {
372+
private int inputNumber;
373+
374+
SLCallbackListener(int _i) {
375+
inputNumber = _i;
376+
}
377+
public void controlEvent(CallbackEvent theEvent) {
378+
//Selecting an item from ScrollableList triggers Broadcast
379+
if (theEvent.getAction() == ControlP5.ACTION_BROADCAST) {
380+
int val = (int)(theEvent.getController()).getValue();
381+
Map bob = ((ScrollableList)theEvent.getController()).getItem(val);
382+
emgJoystickInputs[inputNumber] = (EmgJoystickInput)bob.get("value");
383+
verbosePrint("EmgJoystickInput: " + (theEvent.getController()).getName() + " == " + emgJoystickInputs[inputNumber].getString());
384+
385+
plotChannelLabels[inputNumber] = Integer.toString(emgJoystickInputs[inputNumber].getIndex() + 1);
386+
}
387+
}
388+
}
389+
390+
private void createInputDropdowns() {
391+
//Create the dropdowns in reverse order so that top dropdown draws over bottom dropdown
392+
yNegativeInputDropdown = createEmgJoystickInputDropdown("yNegativeDropdown", emgJoystickInputs[3], 3);
393+
yPositiveInputDropdown = createEmgJoystickInputDropdown("yPositiveDropdown", emgJoystickInputs[2], 2);
394+
xPositiveInputDropdown = createEmgJoystickInputDropdown("xPositiveDropdown", emgJoystickInputs[1], 1);
395+
xNegativeInputDropdown = createEmgJoystickInputDropdown("xNegativeDropdown", emgJoystickInputs[0], 0);
396+
//Add the dropdowns to the list of cp5 elements to check for mouseover
397+
cp5ElementsToCheck.add(xNegativeInputDropdown);
398+
cp5ElementsToCheck.add(xPositiveInputDropdown);
399+
cp5ElementsToCheck.add(yPositiveInputDropdown);
400+
cp5ElementsToCheck.add(yNegativeInputDropdown);
401+
//Create labels for the dropdowns
402+
color labelBG = color(255,255,255,0);
403+
xNegativeInputDropdownLabel = new TextBox("X-", x, y, OPENBCI_DARKBLUE, WHITE, 12, h3, LEFT, TOP);
404+
xPositiveInputDropdownLabel = new TextBox("X+", x, y, OPENBCI_DARKBLUE, WHITE, 12, h3, LEFT, TOP);
405+
yPositiveInputDropdownLabel = new TextBox("Y+", x, y, OPENBCI_DARKBLUE, WHITE, 12, h3, LEFT, TOP);
406+
yNegativeInputDropdownLabel = new TextBox("Y-", x, y, OPENBCI_DARKBLUE, WHITE, 12, h3, LEFT, TOP);
407+
}
408+
409+
private void updateInputDropdownPositions(){
410+
xNegativeInputDropdown.setPosition((int) (x + navH + DROPDOWN_LABEL_WIDTH), (int) (y + navH + 1));
411+
xPositiveInputDropdown.setPosition((int) (x + navH + DROPDOWN_LABEL_WIDTH), (int) (y + navH + DROPDOWN_SPACER + DROPDOWN_HEIGHT));
412+
yPositiveInputDropdown.setPosition((int) (x + w - navH - DROPDOWN_WIDTH), (int) (y + navH + 1));
413+
yNegativeInputDropdown.setPosition((int) (x + w - navH - DROPDOWN_WIDTH), (int) (y + navH + DROPDOWN_SPACER + DROPDOWN_HEIGHT));
414+
xNegativeInputDropdownLabel.setPosition((int) xNegativeInputDropdown.getPosition()[0] - DROPDOWN_LABEL_WIDTH, (int) xNegativeInputDropdown.getPosition()[1]);
415+
xPositiveInputDropdownLabel.setPosition((int) xPositiveInputDropdown.getPosition()[0] - DROPDOWN_LABEL_WIDTH, (int) xPositiveInputDropdown.getPosition()[1]);
416+
yPositiveInputDropdownLabel.setPosition((int) yPositiveInputDropdown.getPosition()[0] - DROPDOWN_LABEL_WIDTH, (int) yPositiveInputDropdown.getPosition()[1]);
417+
yNegativeInputDropdownLabel.setPosition((int) yNegativeInputDropdown.getPosition()[0] - DROPDOWN_LABEL_WIDTH, (int) yNegativeInputDropdown.getPosition()[1]);
418+
}
419+
420+
private void drawInputDropdownLabels() {
421+
xNegativeInputDropdownLabel.draw();
422+
xPositiveInputDropdownLabel.draw();
423+
yPositiveInputDropdownLabel.draw();
424+
yNegativeInputDropdownLabel.draw();
425+
}
426+
317427
};
318428

319429
public void emgJoystickSmoothingDropdown(int n) {
@@ -353,6 +463,57 @@ public enum EmgJoystickSmoothing implements IndexingInterface
353463
return value;
354464
}
355465

466+
private static List<String> getEnumStringsAsList() {
467+
List<String> enumStrings = new ArrayList<String>();
468+
for (IndexingInterface val : vals) {
469+
enumStrings.add(val.getString());
470+
}
471+
return enumStrings;
472+
}
473+
}
474+
475+
public enum EmgJoystickInput implements IndexingInterface
476+
{
477+
CHANNEL_1 (0, "Channel 1", 0),
478+
CHANNEL_2 (1, "Channel 2", 1),
479+
CHANNEL_3 (2, "Channel 3", 2),
480+
CHANNEL_4 (3, "Channel 4", 3),
481+
CHANNEL_5 (4, "Channel 5", 4),
482+
CHANNEL_6 (5, "Channel 6", 5),
483+
CHANNEL_7 (6, "Channel 7", 6),
484+
CHANNEL_8 (7, "Channel 8", 7),
485+
CHANNEL_9 (8, "Channel 9", 8),
486+
CHANNEL_10 (9, "Channel 10", 9),
487+
CHANNEL_11 (10, "Channel 11", 10),
488+
CHANNEL_12 (11, "Channel 12", 11),
489+
CHANNEL_13 (12, "Channel 13", 12),
490+
CHANNEL_14 (13, "Channel 14", 13),
491+
CHANNEL_15 (14, "Channel 15", 14),
492+
CHANNEL_16 (15, "Channel 16", 15);
493+
494+
private int index;
495+
private String name;
496+
private int value;
497+
private static EmgJoystickInput[] vals = values();
498+
499+
EmgJoystickInput(int index, String name, int value) {
500+
this.index = index;
501+
this.name = name;
502+
this.value = value;
503+
}
504+
505+
public int getIndex() {
506+
return index;
507+
}
508+
509+
public String getString() {
510+
return name;
511+
}
512+
513+
public int getValue() {
514+
return value;
515+
}
516+
356517
private static List<String> getEnumStringsAsList() {
357518
List<String> enumStrings = new ArrayList<String>();
358519
for (IndexingInterface val : vals) {

0 commit comments

Comments
 (0)