Commit | Line | Data |
---|---|---|
e73ef85d MS |
1 | import 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 |
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; | |
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 | ||
79ae8245 MS |
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 | ||
bf551144 MS |
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); | |
0e3c5542 MS |
79 | text("FPS: " + (((int)(frameRate * 10)) / 10.), 4, height-6); |
80 | text("Target (-/+):", 50, height-6); | |
81 | fill(#000000); | |
79ae8245 | 82 | rect(104, height-16, 20, 13); |
0e3c5542 MS |
83 | fill(#666666); |
84 | text("" + targetFramerate, 108, height-6); | |
bfff6bc2 | 85 | text("PandaOutput (p):", 134, height-6); |
79ae8245 MS |
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 | } | |
bfff6bc2 | 96 | |
bf551144 MS |
97 | } |
98 | ||
99 | protected int drawObjectList(int yPos, String title, Object[] items, Method stateMethod) { | |
0ba6ac44 MS |
100 | int sz = (items != null) ? items.length : 0; |
101 | return drawObjectList(yPos, title, items, stateMethod, sz, 0); | |
bf551144 | 102 | } |
0ba6ac44 MS |
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 | ||
bf551144 | 108 | protected int drawObjectList(int yPos, String title, Object[] items, String[] names, Method stateMethod) { |
0ba6ac44 MS |
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) { | |
bf551144 MS |
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; | |
0ba6ac44 MS |
123 | int yTop = yPos+6; |
124 | for (int i = scrollPos; i < items.length && i < (scrollPos + scrollLength); ++i) { | |
bf551144 MS |
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 | } | |
0ba6ac44 | 146 | rect(leftPos, yPos+6, w, lineHeight); |
bf551144 MS |
147 | fill(textColor); |
148 | text(names[i], leftTextPos, yPos += lineHeight); | |
149 | even = !even; | |
150 | } | |
0ba6ac44 MS |
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 | ||
bf551144 MS |
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 | } | |
49815cc0 | 194 | |
0e3c5542 | 195 | abstract public void draw(); |
bf551144 MS |
196 | abstract public void mousePressed(); |
197 | abstract public void mouseDragged(); | |
198 | abstract public void mouseReleased(); | |
0ba6ac44 | 199 | abstract public void mouseWheel(int delta); |
bf551144 MS |
200 | } |
201 | ||
202 | /** | |
203 | * UI for control of patterns, transitions, effects. | |
204 | */ | |
205 | class ControlUI extends OverlayUI { | |
49815cc0 MS |
206 | private final String[] patternNames; |
207 | private final String[] transitionNames; | |
208 | private final String[] effectNames; | |
49815cc0 MS |
209 | |
210 | private int firstPatternY; | |
3f8be614 | 211 | private int firstPatternKnobY; |
49815cc0 | 212 | private int firstTransitionY; |
3f8be614 | 213 | private int firstTransitionKnobY; |
49815cc0 | 214 | private int firstEffectY; |
3f8be614 | 215 | private int firstEffectKnobY; |
0ba6ac44 MS |
216 | |
217 | private final int PATTERN_LIST_LENGTH = 8; | |
218 | private int patternScrollPos = 0; | |
3f8be614 | 219 | |
49815cc0 MS |
220 | private int tempoY; |
221 | ||
222 | private Method patternStateMethod; | |
223 | private Method transitionStateMethod; | |
224 | private Method effectStateMethod; | |
b11ff42b | 225 | |
bf551144 | 226 | ControlUI() { |
3f8be614 MS |
227 | patternNames = classNameArray(patterns, "Pattern"); |
228 | transitionNames = classNameArray(transitions, "Transition"); | |
229 | effectNames = classNameArray(effects, "Effect"); | |
230 | ||
49815cc0 MS |
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 | } | |
bf551144 MS |
239 | |
240 | public void draw() { | |
241 | drawLogoAndBackground(); | |
242 | int yPos = 0; | |
49815cc0 | 243 | firstPatternY = yPos + lineHeight + 6; |
0ba6ac44 | 244 | yPos = drawObjectList(yPos, "PATTERN", patterns, patternNames, patternStateMethod, PATTERN_LIST_LENGTH, patternScrollPos); |
3f8be614 MS |
245 | yPos += controlSpacing; |
246 | firstPatternKnobY = yPos; | |
49815cc0 | 247 | int xPos = leftTextPos; |
cc9fcf4b MS |
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)); | |
49815cc0 MS |
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); | |
3f8be614 MS |
258 | yPos += controlSpacing; |
259 | firstTransitionKnobY = yPos; | |
260 | xPos = leftTextPos; | |
cc9fcf4b MS |
261 | for (VirtualTransitionKnob knob : glucose.transitionKnobs) { |
262 | drawKnob(xPos, yPos, knobSize, knob); | |
3f8be614 MS |
263 | xPos += knobSize + knobSpacing; |
264 | } | |
265 | yPos += knobSize + knobLabelHeight; | |
49815cc0 MS |
266 | |
267 | yPos += sectionSpacing; | |
268 | firstEffectY = yPos + lineHeight + 6; | |
269 | yPos = drawObjectList(yPos, "FX", effects, effectNames, effectStateMethod); | |
3f8be614 MS |
270 | yPos += controlSpacing; |
271 | firstEffectKnobY = yPos; | |
272 | xPos = leftTextPos; | |
cc9fcf4b MS |
273 | for (VirtualEffectKnob knob : glucose.effectKnobs) { |
274 | drawKnob(xPos, yPos, knobSize, knob); | |
3f8be614 MS |
275 | xPos += knobSize + knobSpacing; |
276 | } | |
277 | yPos += knobSize + knobLabelHeight; | |
49815cc0 MS |
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 | ||
bf551144 | 291 | drawToggleTip("Tap 'u' to hide"); |
49815cc0 MS |
292 | } |
293 | ||
3f8be614 | 294 | public LXParameter getOrNull(List<LXParameter> items, int index) { |
49815cc0 MS |
295 | if (index < items.size()) { |
296 | return items.get(index); | |
297 | } | |
298 | return null; | |
299 | } | |
300 | ||
49815cc0 MS |
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) { | |
3f8be614 MS |
311 | if (e.isEnabled()) { |
312 | return STATE_PENDING; | |
cc9fcf4b | 313 | } else if (e == glucose.getSelectedEffect()) { |
3f8be614 MS |
314 | return STATE_ACTIVE; |
315 | } | |
316 | return STATE_DEFAULT; | |
49815cc0 MS |
317 | } |
318 | ||
319 | public int getState(LXTransition t) { | |
320 | if (t == lx.getTransition()) { | |
321 | return STATE_PENDING; | |
cc9fcf4b | 322 | } else if (t == glucose.getSelectedTransition()) { |
49815cc0 MS |
323 | return STATE_ACTIVE; |
324 | } | |
325 | return STATE_DEFAULT; | |
326 | } | |
49815cc0 | 327 | |
3f8be614 | 328 | private void drawKnob(int xPos, int yPos, int knobSize, LXParameter knob) { |
49815cc0 MS |
329 | final float knobValue = knob.getValuef(); |
330 | String knobLabel = knob.getLabel(); | |
3f8be614 | 331 | if (knobLabel == null) { |
49815cc0 | 332 | knobLabel = "-"; |
3f8be614 MS |
333 | } else if (knobLabel.length() > 4) { |
334 | knobLabel = knobLabel.substring(0, 4); | |
49815cc0 MS |
335 | } |
336 | ||
337 | ellipseMode(CENTER); | |
87f6fa39 | 338 | noStroke(); |
49815cc0 | 339 | fill(#222222); |
3f8be614 MS |
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; | |
49815cc0 | 347 | fill(lightGreen); |
3f8be614 MS |
348 | arc(xPos + knobSize/2, yPos + knobSize/2, knobSize, knobSize, HALF_PI + knobIndent, endArc); |
349 | ||
3f8be614 MS |
350 | // Mask notch out of knob |
351 | fill(color(0, 0, 30)); | |
352 | beginShape(); | |
4eae387e MS |
353 | vertex(xPos + knobSize/2, yPos + knobSize/2.); |
354 | vertex(xPos + knobSize/2 - 6, yPos + knobSize); | |
355 | vertex(xPos + knobSize/2 + 6, yPos + knobSize); | |
3f8be614 | 356 | endShape(); |
4eae387e MS |
357 | |
358 | // Center circle of knob | |
359 | fill(#333333); | |
360 | ellipse(xPos + knobSize/2, yPos + knobSize/2, knobSize/2, knobSize/2); | |
49815cc0 MS |
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); | |
49815cc0 | 368 | } |
3f8be614 MS |
369 | |
370 | private int patternKnobIndex = -1; | |
371 | private int transitionKnobIndex = -1; | |
372 | private int effectKnobIndex = -1; | |
0ba6ac44 | 373 | private boolean patternScrolling = false; |
49815cc0 | 374 | |
49815cc0 MS |
375 | private int lastY; |
376 | private int releaseEffect = -1; | |
377 | private boolean tempoDown = false; | |
378 | ||
379 | public void mousePressed() { | |
380 | lastY = mouseY; | |
3f8be614 | 381 | patternKnobIndex = transitionKnobIndex = effectKnobIndex = -1; |
49815cc0 | 382 | releaseEffect = -1; |
0ba6ac44 | 383 | patternScrolling = false; |
79ae8245 MS |
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 | ||
49815cc0 MS |
399 | if (mouseY > tempoY) { |
400 | if (mouseY - tempoY < tempoHeight) { | |
401 | lx.tempo.tap(); | |
402 | tempoDown = true; | |
403 | } | |
3f8be614 MS |
404 | } else if ((mouseY >= firstEffectKnobY) && (mouseY < firstEffectKnobY + knobSize + knobLabelHeight)) { |
405 | effectKnobIndex = (mouseX - leftTextPos) / (knobSize + knobSpacing); | |
49815cc0 | 406 | } else if (mouseY > firstEffectY) { |
bf551144 | 407 | int effectIndex = objectClickIndex(firstEffectY); |
49815cc0 | 408 | if (effectIndex < effects.length) { |
cc9fcf4b | 409 | if (effects[effectIndex] == glucose.getSelectedEffect()) { |
49815cc0 MS |
410 | effects[effectIndex].enable(); |
411 | releaseEffect = effectIndex; | |
49815cc0 | 412 | } |
cc9fcf4b | 413 | glucose.setSelectedEffect(effectIndex); |
49815cc0 | 414 | } |
3f8be614 MS |
415 | } else if ((mouseY >= firstTransitionKnobY) && (mouseY < firstTransitionKnobY + knobSize + knobLabelHeight)) { |
416 | transitionKnobIndex = (mouseX - leftTextPos) / (knobSize + knobSpacing); | |
49815cc0 | 417 | } else if (mouseY > firstTransitionY) { |
bf551144 | 418 | int transitionIndex = objectClickIndex(firstTransitionY); |
49815cc0 | 419 | if (transitionIndex < transitions.length) { |
cc9fcf4b | 420 | glucose.setSelectedTransition(transitionIndex); |
49815cc0 | 421 | } |
3f8be614 MS |
422 | } else if ((mouseY >= firstPatternKnobY) && (mouseY < firstPatternKnobY + 2*(knobSize+knobLabelHeight) + knobSpacing)) { |
423 | patternKnobIndex = (mouseX - leftTextPos) / (knobSize + knobSpacing); | |
424 | if (mouseY >= firstPatternKnobY + knobSize + knobLabelHeight + knobSpacing) { | |
cc9fcf4b | 425 | patternKnobIndex += glucose.NUM_PATTERN_KNOBS / 2; |
49815cc0 MS |
426 | } |
427 | } else if (mouseY > firstPatternY) { | |
0ba6ac44 MS |
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 | } | |
49815cc0 MS |
435 | } |
436 | } | |
437 | } | |
438 | ||
0ba6ac44 | 439 | int scrolldy = 0; |
49815cc0 MS |
440 | public void mouseDragged() { |
441 | int dy = lastY - mouseY; | |
0ba6ac44 | 442 | scrolldy += dy; |
49815cc0 | 443 | lastY = mouseY; |
cc9fcf4b MS |
444 | if (patternKnobIndex >= 0 && patternKnobIndex < glucose.NUM_PATTERN_KNOBS) { |
445 | LXParameter p = glucose.patternKnobs.get(patternKnobIndex); | |
3f8be614 | 446 | p.setValue(constrain(p.getValuef() + dy*.01, 0, 1)); |
cc9fcf4b MS |
447 | } else if (effectKnobIndex >= 0 && effectKnobIndex < glucose.NUM_EFFECT_KNOBS) { |
448 | LXParameter p = glucose.effectKnobs.get(effectKnobIndex); | |
3f8be614 | 449 | p.setValue(constrain(p.getValuef() + dy*.01, 0, 1)); |
cc9fcf4b MS |
450 | } else if (transitionKnobIndex >= 0 && transitionKnobIndex < glucose.NUM_TRANSITION_KNOBS) { |
451 | LXParameter p = glucose.transitionKnobs.get(transitionKnobIndex); | |
3f8be614 | 452 | p.setValue(constrain(p.getValuef() + dy*.01, 0, 1)); |
0ba6ac44 MS |
453 | } else if (patternScrolling) { |
454 | int scroll = scrolldy / lineHeight; | |
455 | scrolldy = scrolldy % lineHeight; | |
456 | patternScrollPos = constrain(patternScrollPos - scroll, 0, patterns.length - PATTERN_LIST_LENGTH); | |
49815cc0 MS |
457 | } |
458 | } | |
459 | ||
460 | public void mouseReleased() { | |
0ba6ac44 | 461 | patternScrolling = false; |
49815cc0 MS |
462 | tempoDown = false; |
463 | if (releaseEffect >= 0) { | |
464 | effects[releaseEffect].trigger(); | |
465 | releaseEffect = -1; | |
466 | } | |
467 | } | |
3f8be614 | 468 | |
0ba6ac44 MS |
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 | ||
49815cc0 MS |
478 | } |
479 | ||
bf551144 MS |
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"; | |
2bae07c9 | 488 | private final String MAPPING_MODE_CHANNEL = "Channel"; |
bf551144 | 489 | private final String MAPPING_MODE_SINGLE_CUBE = "Single Cube"; |
2bae07c9 | 490 | |
bf551144 MS |
491 | private final String[] mappingModes = { |
492 | MAPPING_MODE_ALL, | |
2bae07c9 MS |
493 | MAPPING_MODE_CHANNEL, |
494 | MAPPING_MODE_SINGLE_CUBE | |
bf551144 MS |
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; | |
2bae07c9 | 521 | private int channelFieldY; |
bf551144 MS |
522 | private int cubeFieldY; |
523 | private int stripFieldY; | |
524 | ||
525 | private boolean dragCube; | |
526 | private boolean dragStrip; | |
2bae07c9 | 527 | private boolean dragChannel; |
bf551144 MS |
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) { | |
2bae07c9 MS |
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 | } | |
bf551144 MS |
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; | |
2bae07c9 MS |
590 | |
591 | channelFieldY = yPos + lineHeight + 6; | |
592 | yPos = drawValueField(yPos, "CHANNEL ID", mappingTool.channelIndex + 1); | |
593 | yPos += sectionSpacing; | |
bf551144 MS |
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() { | |
2bae07c9 | 627 | dragCube = dragStrip = dragChannel = false; |
bf551144 | 628 | lastY = mouseY; |
79ae8245 MS |
629 | |
630 | if (mouseX < leftPos) { | |
631 | return; | |
632 | } | |
633 | ||
bf551144 MS |
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 | } | |
2bae07c9 MS |
642 | } else if (mouseY >= channelFieldY) { |
643 | if (mouseY < channelFieldY + lineHeight) { | |
644 | dragChannel = true; | |
645 | } | |
bf551144 MS |
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); | |
2bae07c9 MS |
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; | |
bf551144 MS |
666 | } |
667 | } | |
668 | } | |
669 | ||
0ba6ac44 MS |
670 | public void mouseReleased() {} |
671 | public void mouseWheel(int delta) {} | |
bf551144 MS |
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 | } | |
2bae07c9 MS |
690 | } else if (dragChannel) { |
691 | if (dy < 0) { | |
692 | mappingTool.decChannel(); | |
693 | } else { | |
694 | mappingTool.incChannel(); | |
695 | } | |
bf551144 MS |
696 | } |
697 | } | |
698 | ||
699 | } | |
49815cc0 MS |
700 | } |
701 | ||
554e38ff MS |
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(int[][] frontChannels, int[][] rearChannels) { | |
716 | channelList = new int[frontChannels.length + rearChannels.length][]; | |
717 | int channelIndex = 0; | |
718 | for (int[] channel : frontChannels) { | |
719 | channelList[channelIndex++] = channel; | |
720 | } | |
721 | for (int[] channel : rearChannels) { | |
722 | channelList[channelIndex++] = channel; | |
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 | } |