Make pattern list scrollable, since we'll have many now
[SugarCubes.git] / _Overlay.pde
CommitLineData
e73ef85d
MS
1import java.lang.reflect.*;
2
49815cc0 3/**
1ecdb44a
MS
4 * DOUBLE BLACK DIAMOND DOUBLE BLACK DIAMOND
5 *
6 * //\\ //\\ //\\ //\\
7 * ///\\\ ///\\\ ///\\\ ///\\\
8 * \\\/// \\\/// \\\/// \\\///
9 * \\// \\// \\// \\//
10 *
11 * EXPERTS ONLY!! EXPERTS ONLY!!
12 *
49815cc0
MS
13 * Overlay UI that indicates pattern control, etc. This will be moved
14 * into the Processing library once it is stabilized and need not be
15 * regularly modified.
16 */
bf551144
MS
17abstract class OverlayUI {
18 protected final PFont titleFont = createFont("Myriad Pro", 10);
19 protected final color titleColor = #AAAAAA;
20 protected final PFont itemFont = createFont("Lucida Grande", 11);
21 protected final PFont knobFont = titleFont;
22 protected final int w = 140;
23 protected final int leftPos;
24 protected final int leftTextPos;
25 protected final int lineHeight = 20;
26 protected final int sectionSpacing = 12;
27 protected final int controlSpacing = 18;
28 protected final int tempoHeight = 20;
29 protected final int knobSize = 28;
30 protected final float knobIndent = .4;
31 protected final int knobSpacing = 6;
32 protected final int knobLabelHeight = 14;
0ba6ac44 33 protected final int scrollWidth = 14;
bf551144
MS
34 protected final color lightBlue = #666699;
35 protected final color lightGreen = #669966;
49815cc0 36
bf551144
MS
37 private PImage logo;
38
39 protected final int STATE_DEFAULT = 0;
40 protected final int STATE_ACTIVE = 1;
41 protected final int STATE_PENDING = 2;
42
43 protected OverlayUI() {
44 leftPos = width - w;
45 leftTextPos = leftPos + 4;
46 logo = loadImage("logo-sm.png");
47 }
48
49 protected void drawLogoAndBackground() {
50 image(logo, 4, 4);
51 stroke(color(0, 0, 100));
52 // fill(color(0, 0, 50, 50)); // alpha is bad for perf
53 fill(color(0, 0, 30));
54 rect(leftPos-1, -1, w+2, height+2);
55 }
56
57 protected void drawToggleTip(String s) {
58 fill(#999999);
59 textFont(itemFont);
60 textAlign(LEFT);
61 text(s, leftTextPos, height-6);
62 }
63
64 protected void drawHelpTip() {
65 textFont(itemFont);
66 textAlign(RIGHT);
67 text("Tap 'u' to restore UI", width-4, height-6);
68 }
69
70 public void drawFPS() {
71 textFont(titleFont);
72 textAlign(LEFT);
73 fill(#666666);
0e3c5542
MS
74 text("FPS: " + (((int)(frameRate * 10)) / 10.), 4, height-6);
75 text("Target (-/+):", 50, height-6);
76 fill(#000000);
77 rect(104, height-16, 20, 12);
78 fill(#666666);
79 text("" + targetFramerate, 108, height-6);
bfff6bc2
MS
80 text("PandaOutput (p):", 134, height-6);
81 fill(#000000);
82 rect(214, height-16, 26, 12);
83 fill(#666666);
84 text(pandaBoardsEnabled ? "ON" : "OFF", 218, height-6);
85
bf551144
MS
86 }
87
88 protected int drawObjectList(int yPos, String title, Object[] items, Method stateMethod) {
0ba6ac44
MS
89 int sz = (items != null) ? items.length : 0;
90 return drawObjectList(yPos, title, items, stateMethod, sz, 0);
bf551144 91 }
0ba6ac44
MS
92
93 protected int drawObjectList(int yPos, String title, Object[] items, Method stateMethod, int scrollLength, int scrollPos) {
94 return drawObjectList(yPos, title, items, classNameArray(items, null), stateMethod, scrollLength, scrollPos);
95 }
96
bf551144 97 protected int drawObjectList(int yPos, String title, Object[] items, String[] names, Method stateMethod) {
0ba6ac44
MS
98 int sz = (items != null) ? items.length : 0;
99 return drawObjectList(yPos, title, items, names, stateMethod, sz, 0);
100 }
101
102 protected int drawObjectList(int yPos, String title, Object[] items, String[] names, Method stateMethod, int scrollLength, int scrollPos) {
bf551144
MS
103 noStroke();
104 fill(titleColor);
105 textFont(titleFont);
106 textAlign(LEFT);
107 text(title, leftTextPos, yPos += lineHeight);
108 if (items != null) {
109 textFont(itemFont);
110 color textColor;
111 boolean even = true;
0ba6ac44
MS
112 int yTop = yPos+6;
113 for (int i = scrollPos; i < items.length && i < (scrollPos + scrollLength); ++i) {
bf551144
MS
114 Object o = items[i];
115 int state = STATE_DEFAULT;
116 try {
117 state = ((Integer) stateMethod.invoke(this, o)).intValue();
118 } catch (Exception x) {
119 throw new RuntimeException(x);
120 }
121 switch (state) {
122 case STATE_ACTIVE:
123 fill(lightGreen);
124 textColor = #eeeeee;
125 break;
126 case STATE_PENDING:
127 fill(lightBlue);
128 textColor = color(0, 0, 75 + 15*sin(millis()/200.));;
129 break;
130 default:
131 textColor = 0;
132 fill(even ? #666666 : #777777);
133 break;
134 }
0ba6ac44 135 rect(leftPos, yPos+6, w, lineHeight);
bf551144
MS
136 fill(textColor);
137 text(names[i], leftTextPos, yPos += lineHeight);
138 even = !even;
139 }
0ba6ac44
MS
140 if ((scrollPos > 0) || (scrollLength < items.length)) {
141 int yHere = yPos+6;
142 noStroke();
143 fill(color(0, 0, 0, 50));
144 rect(leftPos + w - scrollWidth, yTop, scrollWidth, yHere - yTop);
145 fill(#666666);
146 rect(leftPos + w - scrollWidth + 2, yTop + (yHere-yTop) * (scrollPos / (float)items.length), scrollWidth - 4, (yHere - yTop) * (scrollLength / (float)items.length));
147
148 }
149
bf551144
MS
150 }
151 return yPos;
152 }
153
154 protected String[] classNameArray(Object[] objects, String suffix) {
155 if (objects == null) {
156 return null;
157 }
158 String[] names = new String[objects.length];
159 for (int i = 0; i < objects.length; ++i) {
160 names[i] = className(objects[i], suffix);
161 }
162 return names;
163 }
164
165 protected String className(Object p, String suffix) {
166 String s = p.getClass().getName();
167 int li;
168 if ((li = s.lastIndexOf(".")) > 0) {
169 s = s.substring(li + 1);
170 }
171 if (s.indexOf("SugarCubes$") == 0) {
172 s = s.substring("SugarCubes$".length());
173 }
174 if ((suffix != null) && ((li = s.indexOf(suffix)) != -1)) {
175 s = s.substring(0, li);
176 }
177 return s;
178 }
179
180 protected int objectClickIndex(int firstItemY) {
181 return (mouseY - firstItemY) / lineHeight;
182 }
49815cc0 183
0e3c5542 184 abstract public void draw();
bf551144
MS
185 abstract public void mousePressed();
186 abstract public void mouseDragged();
187 abstract public void mouseReleased();
0ba6ac44 188 abstract public void mouseWheel(int delta);
bf551144
MS
189}
190
191/**
192 * UI for control of patterns, transitions, effects.
193 */
194class ControlUI extends OverlayUI {
49815cc0
MS
195 private final String[] patternNames;
196 private final String[] transitionNames;
197 private final String[] effectNames;
49815cc0
MS
198
199 private int firstPatternY;
3f8be614 200 private int firstPatternKnobY;
49815cc0 201 private int firstTransitionY;
3f8be614 202 private int firstTransitionKnobY;
49815cc0 203 private int firstEffectY;
3f8be614 204 private int firstEffectKnobY;
0ba6ac44
MS
205
206 private final int PATTERN_LIST_LENGTH = 8;
207 private int patternScrollPos = 0;
3f8be614 208
49815cc0
MS
209 private int tempoY;
210
211 private Method patternStateMethod;
212 private Method transitionStateMethod;
213 private Method effectStateMethod;
b11ff42b 214
bf551144 215 ControlUI() {
3f8be614
MS
216 patternNames = classNameArray(patterns, "Pattern");
217 transitionNames = classNameArray(transitions, "Transition");
218 effectNames = classNameArray(effects, "Effect");
219
49815cc0
MS
220 try {
221 patternStateMethod = getClass().getMethod("getState", LXPattern.class);
222 effectStateMethod = getClass().getMethod("getState", LXEffect.class);
223 transitionStateMethod = getClass().getMethod("getState", LXTransition.class);
224 } catch (Exception x) {
225 throw new RuntimeException(x);
226 }
227 }
bf551144
MS
228
229 public void draw() {
230 drawLogoAndBackground();
231 int yPos = 0;
49815cc0 232 firstPatternY = yPos + lineHeight + 6;
0ba6ac44 233 yPos = drawObjectList(yPos, "PATTERN", patterns, patternNames, patternStateMethod, PATTERN_LIST_LENGTH, patternScrollPos);
3f8be614
MS
234 yPos += controlSpacing;
235 firstPatternKnobY = yPos;
49815cc0 236 int xPos = leftTextPos;
cc9fcf4b
MS
237 for (int i = 0; i < glucose.NUM_PATTERN_KNOBS/2; ++i) {
238 drawKnob(xPos, yPos, knobSize, glucose.patternKnobs.get(i));
239 drawKnob(xPos, yPos + knobSize + knobSpacing + knobLabelHeight, knobSize, glucose.patternKnobs.get(glucose.NUM_PATTERN_KNOBS/2 + i));
49815cc0
MS
240 xPos += knobSize + knobSpacing;
241 }
242 yPos += 2*(knobSize + knobLabelHeight) + knobSpacing;
243
244 yPos += sectionSpacing;
245 firstTransitionY = yPos + lineHeight + 6;
246 yPos = drawObjectList(yPos, "TRANSITION", transitions, transitionNames, transitionStateMethod);
3f8be614
MS
247 yPos += controlSpacing;
248 firstTransitionKnobY = yPos;
249 xPos = leftTextPos;
cc9fcf4b
MS
250 for (VirtualTransitionKnob knob : glucose.transitionKnobs) {
251 drawKnob(xPos, yPos, knobSize, knob);
3f8be614
MS
252 xPos += knobSize + knobSpacing;
253 }
254 yPos += knobSize + knobLabelHeight;
49815cc0
MS
255
256 yPos += sectionSpacing;
257 firstEffectY = yPos + lineHeight + 6;
258 yPos = drawObjectList(yPos, "FX", effects, effectNames, effectStateMethod);
3f8be614
MS
259 yPos += controlSpacing;
260 firstEffectKnobY = yPos;
261 xPos = leftTextPos;
cc9fcf4b
MS
262 for (VirtualEffectKnob knob : glucose.effectKnobs) {
263 drawKnob(xPos, yPos, knobSize, knob);
3f8be614
MS
264 xPos += knobSize + knobSpacing;
265 }
266 yPos += knobSize + knobLabelHeight;
49815cc0
MS
267
268 yPos += sectionSpacing;
269 yPos = drawObjectList(yPos, "TEMPO", null, null, null);
270 yPos += 6;
271 tempoY = yPos;
272 stroke(#111111);
273 fill(tempoDown ? lightGreen : color(0, 0, 35 - 8*lx.tempo.rampf()));
274 rect(leftPos + 4, yPos, w - 8, tempoHeight);
275 fill(0);
276 textAlign(CENTER);
277 text("" + ((int)(lx.tempo.bpmf() * 100) / 100.), leftPos + w/2., yPos + tempoHeight - 6);
278 yPos += tempoHeight;
279
bf551144 280 drawToggleTip("Tap 'u' to hide");
49815cc0
MS
281 }
282
3f8be614 283 public LXParameter getOrNull(List<LXParameter> items, int index) {
49815cc0
MS
284 if (index < items.size()) {
285 return items.get(index);
286 }
287 return null;
288 }
289
49815cc0
MS
290 public int getState(LXPattern p) {
291 if (p == lx.getPattern()) {
292 return STATE_ACTIVE;
293 } else if (p == lx.getNextPattern()) {
294 return STATE_PENDING;
295 }
296 return STATE_DEFAULT;
297 }
298
299 public int getState(LXEffect e) {
3f8be614
MS
300 if (e.isEnabled()) {
301 return STATE_PENDING;
cc9fcf4b 302 } else if (e == glucose.getSelectedEffect()) {
3f8be614
MS
303 return STATE_ACTIVE;
304 }
305 return STATE_DEFAULT;
49815cc0
MS
306 }
307
308 public int getState(LXTransition t) {
309 if (t == lx.getTransition()) {
310 return STATE_PENDING;
cc9fcf4b 311 } else if (t == glucose.getSelectedTransition()) {
49815cc0
MS
312 return STATE_ACTIVE;
313 }
314 return STATE_DEFAULT;
315 }
49815cc0 316
3f8be614 317 private void drawKnob(int xPos, int yPos, int knobSize, LXParameter knob) {
49815cc0
MS
318 final float knobValue = knob.getValuef();
319 String knobLabel = knob.getLabel();
3f8be614 320 if (knobLabel == null) {
49815cc0 321 knobLabel = "-";
3f8be614
MS
322 } else if (knobLabel.length() > 4) {
323 knobLabel = knobLabel.substring(0, 4);
49815cc0
MS
324 }
325
326 ellipseMode(CENTER);
87f6fa39 327 noStroke();
49815cc0 328 fill(#222222);
3f8be614
MS
329 // For some reason this arc call really crushes drawing performance. Presumably
330 // because openGL is drawing it and when we overlap the second set of arcs it
331 // does a bunch of depth buffer intersection tests? Ellipse with a trapezoid cut out is faster
332 // arc(xPos + knobSize/2, yPos + knobSize/2, knobSize, knobSize, HALF_PI + knobIndent, HALF_PI + knobIndent + (TWO_PI-2*knobIndent));
333 ellipse(xPos + knobSize/2, yPos + knobSize/2, knobSize, knobSize);
334
335 float endArc = HALF_PI + knobIndent + (TWO_PI-2*knobIndent)*knobValue;
49815cc0 336 fill(lightGreen);
3f8be614
MS
337 arc(xPos + knobSize/2, yPos + knobSize/2, knobSize, knobSize, HALF_PI + knobIndent, endArc);
338
3f8be614
MS
339 // Mask notch out of knob
340 fill(color(0, 0, 30));
341 beginShape();
4eae387e
MS
342 vertex(xPos + knobSize/2, yPos + knobSize/2.);
343 vertex(xPos + knobSize/2 - 6, yPos + knobSize);
344 vertex(xPos + knobSize/2 + 6, yPos + knobSize);
3f8be614 345 endShape();
4eae387e
MS
346
347 // Center circle of knob
348 fill(#333333);
349 ellipse(xPos + knobSize/2, yPos + knobSize/2, knobSize/2, knobSize/2);
49815cc0
MS
350
351 fill(0);
352 rect(xPos, yPos + knobSize + 2, knobSize, knobLabelHeight - 2);
353 fill(#999999);
354 textAlign(CENTER);
355 textFont(knobFont);
356 text(knobLabel, xPos + knobSize/2, yPos + knobSize + knobLabelHeight - 2);
49815cc0 357 }
3f8be614
MS
358
359 private int patternKnobIndex = -1;
360 private int transitionKnobIndex = -1;
361 private int effectKnobIndex = -1;
0ba6ac44 362 private boolean patternScrolling = false;
49815cc0 363
49815cc0
MS
364 private int lastY;
365 private int releaseEffect = -1;
366 private boolean tempoDown = false;
367
368 public void mousePressed() {
369 lastY = mouseY;
3f8be614 370 patternKnobIndex = transitionKnobIndex = effectKnobIndex = -1;
49815cc0 371 releaseEffect = -1;
0ba6ac44 372 patternScrolling = false;
49815cc0
MS
373 if (mouseY > tempoY) {
374 if (mouseY - tempoY < tempoHeight) {
375 lx.tempo.tap();
376 tempoDown = true;
377 }
3f8be614
MS
378 } else if ((mouseY >= firstEffectKnobY) && (mouseY < firstEffectKnobY + knobSize + knobLabelHeight)) {
379 effectKnobIndex = (mouseX - leftTextPos) / (knobSize + knobSpacing);
49815cc0 380 } else if (mouseY > firstEffectY) {
bf551144 381 int effectIndex = objectClickIndex(firstEffectY);
49815cc0 382 if (effectIndex < effects.length) {
cc9fcf4b 383 if (effects[effectIndex] == glucose.getSelectedEffect()) {
49815cc0
MS
384 effects[effectIndex].enable();
385 releaseEffect = effectIndex;
49815cc0 386 }
cc9fcf4b 387 glucose.setSelectedEffect(effectIndex);
49815cc0 388 }
3f8be614
MS
389 } else if ((mouseY >= firstTransitionKnobY) && (mouseY < firstTransitionKnobY + knobSize + knobLabelHeight)) {
390 transitionKnobIndex = (mouseX - leftTextPos) / (knobSize + knobSpacing);
49815cc0 391 } else if (mouseY > firstTransitionY) {
bf551144 392 int transitionIndex = objectClickIndex(firstTransitionY);
49815cc0 393 if (transitionIndex < transitions.length) {
cc9fcf4b 394 glucose.setSelectedTransition(transitionIndex);
49815cc0 395 }
3f8be614
MS
396 } else if ((mouseY >= firstPatternKnobY) && (mouseY < firstPatternKnobY + 2*(knobSize+knobLabelHeight) + knobSpacing)) {
397 patternKnobIndex = (mouseX - leftTextPos) / (knobSize + knobSpacing);
398 if (mouseY >= firstPatternKnobY + knobSize + knobLabelHeight + knobSpacing) {
cc9fcf4b 399 patternKnobIndex += glucose.NUM_PATTERN_KNOBS / 2;
49815cc0
MS
400 }
401 } else if (mouseY > firstPatternY) {
0ba6ac44
MS
402 if ((patterns.length > PATTERN_LIST_LENGTH) && (mouseX > width - scrollWidth)) {
403 patternScrolling = true;
404 } else {
405 int patternIndex = objectClickIndex(firstPatternY);
406 if (patternIndex < patterns.length) {
407 lx.goIndex(patternIndex + patternScrollPos);
408 }
49815cc0
MS
409 }
410 }
411 }
412
0ba6ac44 413 int scrolldy = 0;
49815cc0
MS
414 public void mouseDragged() {
415 int dy = lastY - mouseY;
0ba6ac44 416 scrolldy += dy;
49815cc0 417 lastY = mouseY;
cc9fcf4b
MS
418 if (patternKnobIndex >= 0 && patternKnobIndex < glucose.NUM_PATTERN_KNOBS) {
419 LXParameter p = glucose.patternKnobs.get(patternKnobIndex);
3f8be614 420 p.setValue(constrain(p.getValuef() + dy*.01, 0, 1));
cc9fcf4b
MS
421 } else if (effectKnobIndex >= 0 && effectKnobIndex < glucose.NUM_EFFECT_KNOBS) {
422 LXParameter p = glucose.effectKnobs.get(effectKnobIndex);
3f8be614 423 p.setValue(constrain(p.getValuef() + dy*.01, 0, 1));
cc9fcf4b
MS
424 } else if (transitionKnobIndex >= 0 && transitionKnobIndex < glucose.NUM_TRANSITION_KNOBS) {
425 LXParameter p = glucose.transitionKnobs.get(transitionKnobIndex);
3f8be614 426 p.setValue(constrain(p.getValuef() + dy*.01, 0, 1));
0ba6ac44
MS
427 } else if (patternScrolling) {
428 int scroll = scrolldy / lineHeight;
429 scrolldy = scrolldy % lineHeight;
430 patternScrollPos = constrain(patternScrollPos - scroll, 0, patterns.length - PATTERN_LIST_LENGTH);
49815cc0
MS
431 }
432 }
433
434 public void mouseReleased() {
0ba6ac44 435 patternScrolling = false;
49815cc0
MS
436 tempoDown = false;
437 if (releaseEffect >= 0) {
438 effects[releaseEffect].trigger();
439 releaseEffect = -1;
440 }
441 }
3f8be614 442
0ba6ac44
MS
443 public void mouseWheel(int delta) {
444 if (mouseY > firstPatternY) {
445 int patternIndex = objectClickIndex(firstPatternY);
446 if (patternIndex < PATTERN_LIST_LENGTH) {
447 patternScrollPos = constrain(patternScrollPos + delta, 0, patterns.length - PATTERN_LIST_LENGTH);
448 }
449 }
450 }
451
49815cc0
MS
452}
453
bf551144
MS
454/**
455 * UI for control of mapping.
456 */
457class MappingUI extends OverlayUI {
458
459 private MappingTool mappingTool;
460
461 private final String MAPPING_MODE_ALL = "All On";
2bae07c9 462 private final String MAPPING_MODE_CHANNEL = "Channel";
bf551144 463 private final String MAPPING_MODE_SINGLE_CUBE = "Single Cube";
2bae07c9 464
bf551144
MS
465 private final String[] mappingModes = {
466 MAPPING_MODE_ALL,
2bae07c9
MS
467 MAPPING_MODE_CHANNEL,
468 MAPPING_MODE_SINGLE_CUBE
bf551144
MS
469 };
470 private final Method mappingModeStateMethod;
471
472 private final String CUBE_MODE_ALL = "All Strips";
473 private final String CUBE_MODE_SINGLE_STRIP = "Single Strip";
474 private final String CUBE_MODE_STRIP_PATTERN = "Strip Pattern";
475 private final String[] cubeModes = {
476 CUBE_MODE_ALL,
477 CUBE_MODE_SINGLE_STRIP,
478 CUBE_MODE_STRIP_PATTERN
479 };
480 private final Method cubeModeStateMethod;
481
482 private final String CHANNEL_MODE_RED = "Red";
483 private final String CHANNEL_MODE_GREEN = "Green";
484 private final String CHANNEL_MODE_BLUE = "Blue";
485 private final String[] channelModes = {
486 CHANNEL_MODE_RED,
487 CHANNEL_MODE_GREEN,
488 CHANNEL_MODE_BLUE,
489 };
490 private final Method channelModeStateMethod;
491
492 private int firstMappingY;
493 private int firstCubeY;
494 private int firstChannelY;
2bae07c9 495 private int channelFieldY;
bf551144
MS
496 private int cubeFieldY;
497 private int stripFieldY;
498
499 private boolean dragCube;
500 private boolean dragStrip;
2bae07c9 501 private boolean dragChannel;
bf551144
MS
502
503 MappingUI(MappingTool mappingTool) {
504 this.mappingTool = mappingTool;
505 try {
506 mappingModeStateMethod = getClass().getMethod("getMappingState", Object.class);
507 channelModeStateMethod = getClass().getMethod("getChannelState", Object.class);
508 cubeModeStateMethod = getClass().getMethod("getCubeState", Object.class);
509 } catch (Exception x) {
510 throw new RuntimeException(x);
511 }
512 }
513
514 public int getMappingState(Object mappingMode) {
2bae07c9
MS
515 boolean active = false;
516 if (mappingMode == MAPPING_MODE_ALL) {
517 active = mappingTool.mappingMode == mappingTool.MAPPING_MODE_ALL;
518 } else if (mappingMode == MAPPING_MODE_CHANNEL) {
519 active = mappingTool.mappingMode == mappingTool.MAPPING_MODE_CHANNEL;
520 } else if (mappingMode == MAPPING_MODE_SINGLE_CUBE) {
521 active = mappingTool.mappingMode == mappingTool.MAPPING_MODE_SINGLE_CUBE;
522 }
bf551144
MS
523 return active ? STATE_ACTIVE : STATE_DEFAULT;
524 }
525
526 public int getChannelState(Object channelMode) {
527 boolean active = false;
528 if (channelMode == CHANNEL_MODE_RED) {
529 active = mappingTool.channelModeRed;
530 } else if (channelMode == CHANNEL_MODE_GREEN) {
531 active = mappingTool.channelModeGreen;
532 } else if (channelMode == CHANNEL_MODE_BLUE) {
533 active = mappingTool.channelModeBlue;
534 }
535 return active ? STATE_ACTIVE : STATE_DEFAULT;
536 }
537
538 public int getCubeState(Object cubeMode) {
539 boolean active = false;
540 if (cubeMode == CUBE_MODE_ALL) {
541 active = mappingTool.cubeMode == mappingTool.CUBE_MODE_ALL;
542 } else if (cubeMode == CUBE_MODE_SINGLE_STRIP) {
543 active = mappingTool.cubeMode == mappingTool.CUBE_MODE_SINGLE_STRIP;
544 } else if (cubeMode == CUBE_MODE_STRIP_PATTERN) {
545 active = mappingTool.cubeMode == mappingTool.CUBE_MODE_STRIP_PATTERN;
546 }
547 return active ? STATE_ACTIVE : STATE_DEFAULT;
548 }
549
550 public void draw() {
551 drawLogoAndBackground();
552 int yPos = 0;
553 firstMappingY = yPos + lineHeight + 6;
554 yPos = drawObjectList(yPos, "MAPPING MODE", mappingModes, mappingModes, mappingModeStateMethod);
555 yPos += sectionSpacing;
556
557 firstCubeY = yPos + lineHeight + 6;
558 yPos = drawObjectList(yPos, "CUBE MODE", cubeModes, cubeModes, cubeModeStateMethod);
559 yPos += sectionSpacing;
560
561 firstChannelY = yPos + lineHeight + 6;
562 yPos = drawObjectList(yPos, "CHANNELS", channelModes, channelModes, channelModeStateMethod);
563 yPos += sectionSpacing;
2bae07c9
MS
564
565 channelFieldY = yPos + lineHeight + 6;
566 yPos = drawValueField(yPos, "CHANNEL ID", mappingTool.channelIndex + 1);
567 yPos += sectionSpacing;
bf551144
MS
568
569 cubeFieldY = yPos + lineHeight + 6;
570 yPos = drawValueField(yPos, "CUBE ID", glucose.model.getRawIndexForCube(mappingTool.cubeIndex));
571 yPos += sectionSpacing;
572
573 stripFieldY = yPos + lineHeight + 6;
574 yPos = drawValueField(yPos, "STRIP ID", mappingTool.stripIndex + 1);
575
576 drawToggleTip("Tap 'm' to return");
577 }
578
579 private int drawValueField(int yPos, String label, int value) {
580 yPos += lineHeight;
581 textAlign(LEFT);
582 textFont(titleFont);
583 fill(titleColor);
584 text(label, leftTextPos, yPos);
585 fill(0);
586 yPos += 6;
587 rect(leftTextPos, yPos, w-8, lineHeight);
588 yPos += lineHeight;
589
590 fill(#999999);
591 textAlign(CENTER);
592 textFont(itemFont);
593 text("" + value, leftTextPos + (w-8)/2, yPos - 5);
594
595 return yPos;
596 }
597
598 private int lastY;
599
600 public void mousePressed() {
2bae07c9 601 dragCube = dragStrip = dragChannel = false;
bf551144
MS
602 lastY = mouseY;
603 if (mouseY >= stripFieldY) {
604 if (mouseY < stripFieldY + lineHeight) {
605 dragStrip = true;
606 }
607 } else if (mouseY >= cubeFieldY) {
608 if (mouseY < cubeFieldY + lineHeight) {
609 dragCube = true;
610 }
2bae07c9
MS
611 } else if (mouseY >= channelFieldY) {
612 if (mouseY < channelFieldY + lineHeight) {
613 dragChannel = true;
614 }
bf551144
MS
615 } else if (mouseY >= firstChannelY) {
616 int index = objectClickIndex(firstChannelY);
617 switch (index) {
618 case 0: mappingTool.channelModeRed = !mappingTool.channelModeRed; break;
619 case 1: mappingTool.channelModeGreen = !mappingTool.channelModeGreen; break;
620 case 2: mappingTool.channelModeBlue = !mappingTool.channelModeBlue; break;
621 }
622 } else if (mouseY >= firstCubeY) {
623 int index = objectClickIndex(firstCubeY);
624 switch (index) {
625 case 0: mappingTool.cubeMode = mappingTool.CUBE_MODE_ALL; break;
626 case 1: mappingTool.cubeMode = mappingTool.CUBE_MODE_SINGLE_STRIP; break;
627 case 2: mappingTool.cubeMode = mappingTool.CUBE_MODE_STRIP_PATTERN; break;
628 }
629 } else if (mouseY >= firstMappingY) {
630 int index = objectClickIndex(firstMappingY);
2bae07c9
MS
631 switch (index) {
632 case 0: mappingTool.mappingMode = mappingTool.MAPPING_MODE_ALL; break;
633 case 1: mappingTool.mappingMode = mappingTool.MAPPING_MODE_CHANNEL; break;
634 case 2: mappingTool.mappingMode = mappingTool.MAPPING_MODE_SINGLE_CUBE; break;
bf551144
MS
635 }
636 }
637 }
638
0ba6ac44
MS
639 public void mouseReleased() {}
640 public void mouseWheel(int delta) {}
bf551144
MS
641
642 public void mouseDragged() {
643 final int DRAG_THRESHOLD = 5;
644 int dy = lastY - mouseY;
645 if (abs(dy) >= DRAG_THRESHOLD) {
646 lastY = mouseY;
647 if (dragCube) {
648 if (dy < 0) {
649 mappingTool.decCube();
650 } else {
651 mappingTool.incCube();
652 }
653 } else if (dragStrip) {
654 if (dy < 0) {
655 mappingTool.decStrip();
656 } else {
657 mappingTool.incStrip();
658 }
2bae07c9
MS
659 } else if (dragChannel) {
660 if (dy < 0) {
661 mappingTool.decChannel();
662 } else {
663 mappingTool.incChannel();
664 }
bf551144
MS
665 }
666 }
667
668 }
49815cc0
MS
669}
670
554e38ff
MS
671class DebugUI {
672
673 final int[][] channelList;
674 final int debugX = 10;
675 final int debugY = 42;
676 final int debugXSpacing = 28;
677 final int debugYSpacing = 22;
678 final int[][] debugState = new int[17][6];
679
680 final int DEBUG_STATE_ANIM = 0;
681 final int DEBUG_STATE_WHITE = 1;
682 final int DEBUG_STATE_OFF = 2;
683
684 DebugUI(int[][] frontChannels, int[][] rearChannels) {
685 channelList = new int[frontChannels.length + rearChannels.length][];
686 int channelIndex = 0;
687 for (int[] channel : frontChannels) {
688 channelList[channelIndex++] = channel;
689 }
690 for (int[] channel : rearChannels) {
691 channelList[channelIndex++] = channel;
692 }
693 for (int i = 0; i < debugState.length; ++i) {
694 for (int j = 0; j < debugState[i].length; ++j) {
695 debugState[i][j] = DEBUG_STATE_ANIM;
696 }
697 }
698 }
699
700 void draw() {
701 noStroke();
702 int xBase = debugX;
703 int yPos = debugY;
704
705 fill(color(0, 0, 0, 80));
706 rect(4, 32, 172, 388);
707
708 int channelNum = 0;
709 for (int[] channel : channelList) {
710 int xPos = xBase;
711 drawNumBox(xPos, yPos, channelNum+1, debugState[channelNum][0]);
712
713 boolean first = true;
714 int cubeNum = 0;
715 for (int cube : channel) {
716 if (cube == 0) {
717 break;
718 }
719 xPos += debugXSpacing;
720 if (first) {
721 first = false;
722 } else {
723 stroke(#999999);
724 line(xPos - 12, yPos + 8, xPos, yPos + 8);
725 }
726 drawNumBox(xPos, yPos, cube, debugState[channelNum][cubeNum+1]);
727 ++cubeNum;
728 }
729
730 yPos += debugYSpacing;
731 ++channelNum;
732 }
733 drawNumBox(xBase, yPos, "A", debugState[channelNum][0]);
734 }
735
736 void drawNumBox(int xPos, int yPos, int label, int state) {
737 drawNumBox(xPos, yPos, "" + label, state);
738 }
739
740 void drawNumBox(int xPos, int yPos, String label, int state) {
741 noFill();
742 color textColor = #cccccc;
743 switch (state) {
744 case DEBUG_STATE_ANIM:
745 noStroke();
746 fill(#880000);
747 rect(xPos, yPos, 16, 8);
748 fill(#000088);
749 rect(xPos, yPos+8, 16, 8);
750 noFill();
751 stroke(textColor);
752 rect(xPos, yPos, 16, 16);
753 break;
754 case DEBUG_STATE_WHITE:
755 stroke(textColor);
756 fill(#e9e9e9);
757 rect(xPos, yPos, 16, 16);
758 textColor = #333333;
759 break;
760 case DEBUG_STATE_OFF:
761 stroke(textColor);
762 rect(xPos, yPos, 16, 16);
763 break;
764 }
765
766 noStroke();
767 fill(textColor);
768 text(label, xPos + 2, yPos + 12);
769
770 }
771
772 void maskColors(color[] colors) {
773 color white = #FFFFFF;
774 color off = #000000;
775 int channelIndex = 0;
776 for (int[] channel : channelList) {
777 int cubeIndex = 1;
778 for (int rawCubeIndex : channel) {
779 if (rawCubeIndex > 0) {
780 int state = debugState[channelIndex][cubeIndex];
781 if (state != DEBUG_STATE_ANIM) {
782 color debugColor = (state == DEBUG_STATE_WHITE) ? white : off;
783 Cube cube = glucose.model.getCubeByRawIndex(rawCubeIndex);
784 for (Point p : cube.points) {
785 colors[p.index] = debugColor;
786 }
787 }
788 }
789 ++cubeIndex;
790 }
791 ++channelIndex;
792 }
793 }
794
795 void mousePressed() {
796 int dx = (mouseX - debugX) / debugXSpacing;
797 int dy = (mouseY - debugY) / debugYSpacing;
798 if ((dy >= 0) && (dy < debugState.length)) {
799 if ((dx >= 0) && (dx < debugState[dy].length)) {
800 int newState = debugState[dy][dx] = (debugState[dy][dx] + 1) % 3;
801 if (dy == 16) {
802 for (int[] states : debugState) {
803 for (int i = 0; i < states.length; ++i) {
804 states[i] = newState;
805 }
806 }
807 } else if (dx == 0) {
808 for (int i = 0; i < debugState[dy].length; ++i) {
809 debugState[dy][i] = newState;
810 }
811 }
812 }
813 }
814 }
815}