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