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