Add mappings for panda board UI and PandaDriver class
[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 */
17class OverlayUI {
18
19 private final PFont titleFont = createFont("Myriad Pro", 10);
3f8be614 20 private final PFont itemFont = createFont("Lucida Grande", 11);
49815cc0
MS
21 private final PFont knobFont = titleFont;
22 private final int w = 140;
23 private final int leftPos;
24 private final int leftTextPos;
25 private final int lineHeight = 20;
26 private final int sectionSpacing = 12;
3f8be614 27 private final int controlSpacing = 18;
49815cc0
MS
28 private final int tempoHeight = 20;
29 private final int knobSize = 28;
3f8be614 30 private final float knobIndent = .4;
49815cc0
MS
31 private final int knobSpacing = 6;
32 private final int knobLabelHeight = 14;
33 private final color lightBlue = #666699;
34 private final color lightGreen = #669966;
35
36 private final String[] patternNames;
37 private final String[] transitionNames;
38 private final String[] effectNames;
39
40 private PImage logo;
41
42 private int firstPatternY;
3f8be614 43 private int firstPatternKnobY;
49815cc0 44 private int firstTransitionY;
3f8be614 45 private int firstTransitionKnobY;
49815cc0 46 private int firstEffectY;
3f8be614
MS
47 private int firstEffectKnobY;
48
49815cc0
MS
49 private int tempoY;
50
51 private Method patternStateMethod;
52 private Method transitionStateMethod;
53 private Method effectStateMethod;
54
809f3518 55 private final int NUM_PATTERN_KNOBS = 8;
3f8be614
MS
56 private final int NUM_TRANSITION_KNOBS = 4;
57 private final int NUM_EFFECT_KNOBS = 4;
58
59 private int activeTransitionIndex = 0;
60 private int activeEffectIndex = 0;
61
809f3518
MS
62 public final VirtualPatternKnob[] patternKnobs;
63 public final VirtualTransitionKnob[] transitionKnobs;
64 public final VirtualEffectKnob[] effectKnobs;
3f8be614 65
49815cc0
MS
66 OverlayUI() {
67 leftPos = width - w;
68 leftTextPos = leftPos + 4;
69 logo = loadImage("logo-sm.png");
70
3f8be614
MS
71 patternNames = classNameArray(patterns, "Pattern");
72 transitionNames = classNameArray(transitions, "Transition");
73 effectNames = classNameArray(effects, "Effect");
74
809f3518
MS
75 patternKnobs = new VirtualPatternKnob[NUM_PATTERN_KNOBS];
76 for (int i = 0; i < patternKnobs.length; ++i) {
77 patternKnobs[i] = new VirtualPatternKnob(i);
78 }
79
3f8be614
MS
80 transitionKnobs = new VirtualTransitionKnob[NUM_TRANSITION_KNOBS];
81 for (int i = 0; i < transitionKnobs.length; ++i) {
82 transitionKnobs[i] = new VirtualTransitionKnob(i);
83 }
84
85 effectKnobs = new VirtualEffectKnob[NUM_EFFECT_KNOBS];
86 for (int i = 0; i < effectKnobs.length; ++i) {
87 effectKnobs[i] = new VirtualEffectKnob(i);
88 }
49815cc0
MS
89
90 try {
91 patternStateMethod = getClass().getMethod("getState", LXPattern.class);
92 effectStateMethod = getClass().getMethod("getState", LXEffect.class);
93 transitionStateMethod = getClass().getMethod("getState", LXTransition.class);
94 } catch (Exception x) {
95 throw new RuntimeException(x);
96 }
97 }
98
99 void drawHelpTip() {
100 textFont(itemFont);
101 textAlign(RIGHT);
102 text("Tap 'u' to restore UI", width-4, height-6);
103 }
104
105 void draw() {
106 image(logo, 4, 4);
107
108 stroke(color(0, 0, 100));
109 // fill(color(0, 0, 50, 50)); // alpha is bad for perf
110 fill(color(0, 0, 30));
111 rect(leftPos-1, -1, w+2, height+2);
112
113 int yPos = 0;
114 firstPatternY = yPos + lineHeight + 6;
115 yPos = drawObjectList(yPos, "PATTERN", patterns, patternNames, patternStateMethod);
3f8be614
MS
116 yPos += controlSpacing;
117 firstPatternKnobY = yPos;
49815cc0 118 int xPos = leftTextPos;
809f3518
MS
119 for (int i = 0; i < NUM_PATTERN_KNOBS/2; ++i) {
120 drawKnob(xPos, yPos, knobSize, patternKnobs[i]);
121 drawKnob(xPos, yPos + knobSize + knobSpacing + knobLabelHeight, knobSize, patternKnobs[NUM_PATTERN_KNOBS/2 + i]);
49815cc0
MS
122 xPos += knobSize + knobSpacing;
123 }
124 yPos += 2*(knobSize + knobLabelHeight) + knobSpacing;
125
126 yPos += sectionSpacing;
127 firstTransitionY = yPos + lineHeight + 6;
128 yPos = drawObjectList(yPos, "TRANSITION", transitions, transitionNames, transitionStateMethod);
3f8be614
MS
129 yPos += controlSpacing;
130 firstTransitionKnobY = yPos;
131 xPos = leftTextPos;
132 for (int i = 0; i < transitionKnobs.length; ++i) {
133 drawKnob(xPos, yPos, knobSize, transitionKnobs[i]);
134 xPos += knobSize + knobSpacing;
135 }
136 yPos += knobSize + knobLabelHeight;
49815cc0
MS
137
138 yPos += sectionSpacing;
139 firstEffectY = yPos + lineHeight + 6;
140 yPos = drawObjectList(yPos, "FX", effects, effectNames, effectStateMethod);
3f8be614
MS
141 yPos += controlSpacing;
142 firstEffectKnobY = yPos;
143 xPos = leftTextPos;
144 for (int i = 0; i < effectKnobs.length; ++i) {
145 drawKnob(xPos, yPos, knobSize, effectKnobs[i]);
146 xPos += knobSize + knobSpacing;
147 }
148 yPos += knobSize + knobLabelHeight;
49815cc0
MS
149
150 yPos += sectionSpacing;
151 yPos = drawObjectList(yPos, "TEMPO", null, null, null);
152 yPos += 6;
153 tempoY = yPos;
154 stroke(#111111);
155 fill(tempoDown ? lightGreen : color(0, 0, 35 - 8*lx.tempo.rampf()));
156 rect(leftPos + 4, yPos, w - 8, tempoHeight);
157 fill(0);
158 textAlign(CENTER);
159 text("" + ((int)(lx.tempo.bpmf() * 100) / 100.), leftPos + w/2., yPos + tempoHeight - 6);
160 yPos += tempoHeight;
161
162 fill(#999999);
163 textFont(itemFont);
164 textAlign(LEFT);
e8f5282d 165 text("Tap 'u' to hide UI", leftTextPos, height-6);
49815cc0
MS
166 }
167
3f8be614 168 public LXParameter getOrNull(List<LXParameter> items, int index) {
49815cc0
MS
169 if (index < items.size()) {
170 return items.get(index);
171 }
172 return null;
173 }
174
175 public void drawFPS() {
176 textFont(titleFont);
177 textAlign(LEFT);
178 fill(#666666);
179 text("FPS: " + (((int)(frameRate * 10)) / 10.), 4, height-6);
180 }
181
182 private final int STATE_DEFAULT = 0;
183 private final int STATE_ACTIVE = 1;
184 private final int STATE_PENDING = 2;
185
186 public int getState(LXPattern p) {
187 if (p == lx.getPattern()) {
188 return STATE_ACTIVE;
189 } else if (p == lx.getNextPattern()) {
190 return STATE_PENDING;
191 }
192 return STATE_DEFAULT;
193 }
194
195 public int getState(LXEffect e) {
3f8be614
MS
196 if (e.isEnabled()) {
197 return STATE_PENDING;
198 } else if (effects[activeEffectIndex] == e) {
199 return STATE_ACTIVE;
200 }
201 return STATE_DEFAULT;
49815cc0
MS
202 }
203
204 public int getState(LXTransition t) {
205 if (t == lx.getTransition()) {
206 return STATE_PENDING;
207 } else if (t == transitions[activeTransitionIndex]) {
208 return STATE_ACTIVE;
209 }
210 return STATE_DEFAULT;
211 }
212
213 protected int drawObjectList(int yPos, String title, Object[] items, Method stateMethod) {
3f8be614 214 return drawObjectList(yPos, title, items, classNameArray(items, null), stateMethod);
49815cc0
MS
215 }
216
217 private int drawObjectList(int yPos, String title, Object[] items, String[] names, Method stateMethod) {
218 noStroke();
219 fill(#aaaaaa);
220 textFont(titleFont);
221 textAlign(LEFT);
222 text(title, leftTextPos, yPos += lineHeight);
223 if (items != null) {
224 textFont(itemFont);
225 color textColor;
226 boolean even = true;
227 for (int i = 0; i < items.length; ++i) {
228 Object o = items[i];
229 int state = STATE_DEFAULT;
230 try {
231 state = ((Integer) stateMethod.invoke(this, o)).intValue();
232 } catch (Exception x) {
233 throw new RuntimeException(x);
234 }
235 switch (state) {
236 case STATE_ACTIVE:
237 fill(lightGreen);
238 textColor = #eeeeee;
239 break;
240 case STATE_PENDING:
241 fill(lightBlue);
242 textColor = color(0, 0, 75 + 15*sin(millis()/200.));;
243 break;
244 default:
245 textColor = 0;
246 fill(even ? #666666 : #777777);
247 break;
248 }
249 rect(leftPos, yPos+6, width, lineHeight);
250 fill(textColor);
251 text(names[i], leftTextPos, yPos += lineHeight);
252 even = !even;
253 }
254 }
255 return yPos;
256 }
257
3f8be614
MS
258 private void drawKnob(int xPos, int yPos, int knobSize, LXParameter knob) {
259 if (!knobsOn) {
260 return;
261 }
49815cc0
MS
262 final float knobValue = knob.getValuef();
263 String knobLabel = knob.getLabel();
3f8be614 264 if (knobLabel == null) {
49815cc0 265 knobLabel = "-";
3f8be614
MS
266 } else if (knobLabel.length() > 4) {
267 knobLabel = knobLabel.substring(0, 4);
49815cc0
MS
268 }
269
270 ellipseMode(CENTER);
87f6fa39 271 noStroke();
49815cc0 272 fill(#222222);
3f8be614
MS
273 // For some reason this arc call really crushes drawing performance. Presumably
274 // because openGL is drawing it and when we overlap the second set of arcs it
275 // does a bunch of depth buffer intersection tests? Ellipse with a trapezoid cut out is faster
276 // arc(xPos + knobSize/2, yPos + knobSize/2, knobSize, knobSize, HALF_PI + knobIndent, HALF_PI + knobIndent + (TWO_PI-2*knobIndent));
277 ellipse(xPos + knobSize/2, yPos + knobSize/2, knobSize, knobSize);
278
279 float endArc = HALF_PI + knobIndent + (TWO_PI-2*knobIndent)*knobValue;
49815cc0 280 fill(lightGreen);
3f8be614
MS
281 arc(xPos + knobSize/2, yPos + knobSize/2, knobSize, knobSize, HALF_PI + knobIndent, endArc);
282
3f8be614
MS
283 // Mask notch out of knob
284 fill(color(0, 0, 30));
285 beginShape();
4eae387e
MS
286 vertex(xPos + knobSize/2, yPos + knobSize/2.);
287 vertex(xPos + knobSize/2 - 6, yPos + knobSize);
288 vertex(xPos + knobSize/2 + 6, yPos + knobSize);
3f8be614 289 endShape();
4eae387e
MS
290
291 // Center circle of knob
292 fill(#333333);
293 ellipse(xPos + knobSize/2, yPos + knobSize/2, knobSize/2, knobSize/2);
49815cc0
MS
294
295 fill(0);
296 rect(xPos, yPos + knobSize + 2, knobSize, knobLabelHeight - 2);
297 fill(#999999);
298 textAlign(CENTER);
299 textFont(knobFont);
300 text(knobLabel, xPos + knobSize/2, yPos + knobSize + knobLabelHeight - 2);
301
302 }
303
3f8be614 304 private String[] classNameArray(Object[] objects, String suffix) {
49815cc0
MS
305 if (objects == null) {
306 return null;
307 }
308 String[] names = new String[objects.length];
309 for (int i = 0; i < objects.length; ++i) {
3f8be614 310 names[i] = className(objects[i], suffix);
49815cc0
MS
311 }
312 return names;
313 }
314
3f8be614 315 private String className(Object p, String suffix) {
49815cc0
MS
316 String s = p.getClass().getName();
317 int li;
318 if ((li = s.lastIndexOf(".")) > 0) {
319 s = s.substring(li + 1);
320 }
321 if (s.indexOf("SugarCubes$") == 0) {
3f8be614
MS
322 s = s.substring("SugarCubes$".length());
323 }
324 if ((suffix != null) && ((li = s.indexOf(suffix)) != -1)) {
325 s = s.substring(0, li);
49815cc0
MS
326 }
327 return s;
328 }
3f8be614 329
809f3518
MS
330 class VirtualPatternKnob extends LXVirtualParameter {
331 private final int index;
332
333 VirtualPatternKnob(int index) {
334 this.index = index;
335 }
336
337 public LXParameter getRealParameter() {
338 List<LXParameter> parameters = glucose.getPattern().getParameters();
339 if (index < parameters.size()) {
340 return parameters.get(index);
341 }
342 return null;
343 }
344 }
345
3f8be614
MS
346 class VirtualTransitionKnob extends LXVirtualParameter {
347 private final int index;
348
349 VirtualTransitionKnob(int index) {
350 this.index = index;
351 }
352
353 public LXParameter getRealParameter() {
354 List<LXParameter> parameters = transitions[activeTransitionIndex].getParameters();
355 if (index < parameters.size()) {
356 return parameters.get(index);
357 }
358 return null;
359 }
360 }
361
362 class VirtualEffectKnob extends LXVirtualParameter {
363 private final int index;
364
365 VirtualEffectKnob(int index) {
366 this.index = index;
367 }
368
369 public LXParameter getRealParameter() {
370 List<LXParameter> parameters = effects[activeEffectIndex].getParameters();
371 if (index < parameters.size()) {
372 return parameters.get(index);
373 }
374 return null;
375 }
376 }
377
378 private int patternKnobIndex = -1;
379 private int transitionKnobIndex = -1;
380 private int effectKnobIndex = -1;
49815cc0 381
49815cc0
MS
382 private int lastY;
383 private int releaseEffect = -1;
384 private boolean tempoDown = false;
385
386 public void mousePressed() {
387 lastY = mouseY;
3f8be614 388 patternKnobIndex = transitionKnobIndex = effectKnobIndex = -1;
49815cc0
MS
389 releaseEffect = -1;
390 if (mouseY > tempoY) {
391 if (mouseY - tempoY < tempoHeight) {
392 lx.tempo.tap();
393 tempoDown = true;
394 }
3f8be614
MS
395 } else if ((mouseY >= firstEffectKnobY) && (mouseY < firstEffectKnobY + knobSize + knobLabelHeight)) {
396 effectKnobIndex = (mouseX - leftTextPos) / (knobSize + knobSpacing);
49815cc0
MS
397 } else if (mouseY > firstEffectY) {
398 int effectIndex = (mouseY - firstEffectY) / lineHeight;
399 if (effectIndex < effects.length) {
3f8be614 400 if (activeEffectIndex == effectIndex) {
49815cc0
MS
401 effects[effectIndex].enable();
402 releaseEffect = effectIndex;
49815cc0 403 }
3f8be614 404 activeEffectIndex = effectIndex;
49815cc0 405 }
3f8be614
MS
406 } else if ((mouseY >= firstTransitionKnobY) && (mouseY < firstTransitionKnobY + knobSize + knobLabelHeight)) {
407 transitionKnobIndex = (mouseX - leftTextPos) / (knobSize + knobSpacing);
49815cc0
MS
408 } else if (mouseY > firstTransitionY) {
409 int transitionIndex = (mouseY - firstTransitionY) / lineHeight;
410 if (transitionIndex < transitions.length) {
411 activeTransitionIndex = transitionIndex;
412 }
3f8be614
MS
413 } else if ((mouseY >= firstPatternKnobY) && (mouseY < firstPatternKnobY + 2*(knobSize+knobLabelHeight) + knobSpacing)) {
414 patternKnobIndex = (mouseX - leftTextPos) / (knobSize + knobSpacing);
415 if (mouseY >= firstPatternKnobY + knobSize + knobLabelHeight + knobSpacing) {
809f3518 416 patternKnobIndex += NUM_PATTERN_KNOBS / 2;
49815cc0
MS
417 }
418 } else if (mouseY > firstPatternY) {
419 int patternIndex = (mouseY - firstPatternY) / lineHeight;
420 if (patternIndex < patterns.length) {
421 patterns[patternIndex].setTransition(transitions[activeTransitionIndex]);
422 lx.goIndex(patternIndex);
423 }
424 }
425 }
426
427 public void mouseDragged() {
428 int dy = lastY - mouseY;
429 lastY = mouseY;
809f3518
MS
430 if (patternKnobIndex >= 0 && patternKnobIndex < NUM_PATTERN_KNOBS) {
431 LXParameter p = patternKnobs[patternKnobIndex];
3f8be614
MS
432 p.setValue(constrain(p.getValuef() + dy*.01, 0, 1));
433 } else if (effectKnobIndex >= 0 && effectKnobIndex < NUM_EFFECT_KNOBS) {
434 LXParameter p = effectKnobs[effectKnobIndex];
435 p.setValue(constrain(p.getValuef() + dy*.01, 0, 1));
436 } else if (transitionKnobIndex >= 0 && transitionKnobIndex < NUM_TRANSITION_KNOBS) {
437 LXParameter p = transitionKnobs[transitionKnobIndex];
438 p.setValue(constrain(p.getValuef() + dy*.01, 0, 1));
49815cc0
MS
439 }
440 }
441
442 public void mouseReleased() {
443 tempoDown = false;
444 if (releaseEffect >= 0) {
445 effects[releaseEffect].trigger();
446 releaseEffect = -1;
447 }
448 }
3f8be614 449
49815cc0
MS
450}
451
452void mousePressed() {
453 if (mouseX > ui.leftPos) {
454 ui.mousePressed();
455 }
456}
457
458void mouseReleased() {
459 if (mouseX > ui.leftPos) {
460 ui.mouseReleased();
461 }
462}
463
464void mouseDragged() {
465 if (mouseX > ui.leftPos) {
466 ui.mouseDragged();
467 }
468}
469