X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=_Overlay.pde;h=f1bc1d4a8a9df06038cb7a924772c98d38fa7373;hb=d33c2d1d64234dd5f54942a9b76afaec3833c206;hp=acf8505f7dc995833ecd67681dbb2a7e6627b7e3;hpb=e73ef85d13e1f4787bbf2705d294791d268d95b1;p=SugarCubes.git diff --git a/_Overlay.pde b/_Overlay.pde index acf8505..f1bc1d4 100644 --- a/_Overlay.pde +++ b/_Overlay.pde @@ -14,30 +14,235 @@ import java.lang.reflect.*; * into the Processing library once it is stabilized and need not be * regularly modified. */ -class OverlayUI { - - private final PFont titleFont = createFont("Myriad Pro", 10); - private final PFont itemFont = createFont("Lucida Grande", 11); - private final PFont knobFont = titleFont; - private final int w = 140; - private final int leftPos; - private final int leftTextPos; - private final int lineHeight = 20; - private final int sectionSpacing = 12; - private final int controlSpacing = 18; - private final int tempoHeight = 20; - private final int knobSize = 28; - private final float knobIndent = .4; - private final int knobSpacing = 6; - private final int knobLabelHeight = 14; - private final color lightBlue = #666699; - private final color lightGreen = #669966; +abstract class OverlayUI { + protected final PFont titleFont = createFont("Myriad Pro", 10); + protected final color titleColor = #AAAAAA; + protected final PFont itemFont = createFont("Lucida Grande", 11); + protected final PFont knobFont = titleFont; + protected final int w = 140; + protected final int leftPos; + protected final int leftTextPos; + protected final int lineHeight = 20; + protected final int sectionSpacing = 12; + protected final int controlSpacing = 18; + protected final int tempoHeight = 20; + protected final int knobSize = 28; + protected final float knobIndent = .4; + protected final int knobSpacing = 6; + protected final int knobLabelHeight = 14; + protected final int scrollWidth = 14; + protected final color lightBlue = #666699; + protected final color lightGreen = #669966; + protected final int toggleButtonSize = 10; + private PImage logo; + + protected final int STATE_DEFAULT = 0; + protected final int STATE_ACTIVE = 1; + protected final int STATE_PENDING = 2; + + protected int[] pandaLeft = new int[pandaBoards.length]; + protected final int pandaWidth = 64; + protected final int pandaHeight = 13; + protected final int pandaTop = height-16; + + protected int eligibleLeft; + + protected OverlayUI() { + leftPos = width - w; + leftTextPos = leftPos + 4; + logo = loadImage("logo-sm.png"); + } + + protected void drawLogoAndBackground() { + image(logo, 4, 4); + stroke(color(0, 0, 100)); + // fill(color(0, 0, 50, 50)); // alpha is bad for perf + fill(color(0, 0, 30)); + rect(leftPos-1, -1, w+2, height+2); + } + + protected void drawToggleTip(String s) { + fill(#999999); + textFont(itemFont); + textAlign(LEFT); + text(s, leftTextPos, height-6); + } + + protected void drawHelpTip() { + textFont(itemFont); + textAlign(RIGHT); + text("Tap 'u' to restore UI", width-4, height-6); + } + + public void drawDanText() { + textFont(itemFont); + textAlign(LEFT); + fill(#FFFFFF); + text(DanTextLine1, 4, height-50); + text(DanTextLine2, 4, height-30); + } + + public void drawFPS() { + textFont(titleFont); + textAlign(LEFT); + fill(#666666); + int lPos = 4; + String fps = "FPS: " + (((int)(frameRate * 10)) / 10.); + text(fps, lPos, height-6); + lPos += 48; + + String target = "Target (-/+):"; + text(target, lPos, height-6); + fill(#000000); + lPos += textWidth(target) + 4; + rect(lPos, height-16, 24, 13); + fill(#666666); + text("" + targetFramerate, lPos + (24 - textWidth("" + targetFramerate))/2, height-6); + lPos += 32; + String pandaOutput = "PandaOutput (p):"; + text(pandaOutput, lPos, height-6); + lPos += textWidth(pandaOutput)+4; + int pi = 0; + for (PandaDriver p : pandaBoards) { + pandaLeft[pi++] = lPos; + fill(p.enabled ? #666666 : #000000); + rect(lPos, pandaTop, pandaWidth, pandaHeight); + fill(p.enabled ? #000000 : #666666); + text(p.ip, lPos + (pandaWidth - textWidth(p.ip)) / 2, height-6); + lPos += pandaWidth + 8; + } + + } + + protected int drawObjectList(int yPos, String title, Object[] items, Method stateMethod) { + int sz = (items != null) ? items.length : 0; + return drawObjectList(yPos, title, items, stateMethod, sz, 0); + } + + protected int drawObjectList(int yPos, String title, Object[] items, Method stateMethod, int scrollLength, int scrollPos) { + return drawObjectList(yPos, title, items, classNameArray(items, null), stateMethod, scrollLength, scrollPos); + } + + protected int drawObjectList(int yPos, String title, Object[] items, String[] names, Method stateMethod) { + int sz = (items != null) ? items.length : 0; + return drawObjectList(yPos, title, items, names, stateMethod, sz, 0); + } + + protected void drawToggleButton(float x, float y, boolean eligible, color textColor) { + noFill(); + stroke(textColor); + rect(x, y, toggleButtonSize, toggleButtonSize); + if (eligible) { + noStroke(); + fill(textColor); + rect(x + 2, y + 2, toggleButtonSize - 4, toggleButtonSize - 4); + } + } + + protected int drawObjectList(int yPos, String title, Object[] items, String[] names, Method stateMethod, int scrollLength, int scrollPos) { + noStroke(); + fill(titleColor); + textFont(titleFont); + textAlign(LEFT); + text(title, leftTextPos, yPos += lineHeight); + if (items != null) { + boolean hasScroll = (scrollPos > 0) || (scrollLength < items.length); + textFont(itemFont); + color textColor; + boolean even = true; + int yTop = yPos+6; + for (int i = scrollPos; i < items.length && i < (scrollPos + scrollLength); ++i) { + Object o = items[i]; + int state = STATE_DEFAULT; + try { + state = ((Integer) stateMethod.invoke(this, o)).intValue(); + } catch (Exception x) { + throw new RuntimeException(x); + } + switch (state) { + case STATE_ACTIVE: + fill(lightGreen); + textColor = #eeeeee; + break; + case STATE_PENDING: + fill(lightBlue); + textColor = color(0, 0, 75 + 15*sin(millis()/200.));; + break; + default: + textColor = 0; + fill(even ? #666666 : #777777); + break; + } + noStroke(); + rect(leftPos, yPos+6, w, lineHeight); + fill(textColor); + text(names[i], leftTextPos, yPos += lineHeight); + if (lx.isAutoTransitionEnabled() && items[i] instanceof LXPattern) { + boolean eligible = ((LXPattern)items[i]).isEligible(); + eligibleLeft = leftPos + w - (hasScroll ? scrollWidth : 0) - 15; + drawToggleButton(eligibleLeft, yPos-8, eligible, textColor); + } + even = !even; + } + if (hasScroll) { + int yHere = yPos+6; + noStroke(); + fill(color(0, 0, 0, 50)); + rect(leftPos + w - scrollWidth, yTop, scrollWidth, yHere - yTop); + fill(#666666); + rect(leftPos + w - scrollWidth + 2, yTop + (yHere-yTop) * (scrollPos / (float)items.length), scrollWidth - 4, (yHere - yTop) * (scrollLength / (float)items.length)); + + } + + } + return yPos; + } + + protected String[] classNameArray(Object[] objects, String suffix) { + if (objects == null) { + return null; + } + String[] names = new String[objects.length]; + for (int i = 0; i < objects.length; ++i) { + names[i] = className(objects[i], suffix); + } + return names; + } + + protected String className(Object p, String suffix) { + String s = p.getClass().getName(); + int li; + if ((li = s.lastIndexOf(".")) > 0) { + s = s.substring(li + 1); + } + if (s.indexOf("SugarCubes$") == 0) { + s = s.substring("SugarCubes$".length()); + } + if ((suffix != null) && ((li = s.indexOf(suffix)) != -1)) { + s = s.substring(0, li); + } + return s; + } + + protected int objectClickIndex(int firstItemY) { + return (mouseY - firstItemY) / lineHeight; + } + + abstract public void draw(); + abstract public void mousePressed(); + abstract public void mouseDragged(); + abstract public void mouseReleased(); + abstract public void mouseWheel(int delta); +} + +/** + * UI for control of patterns, transitions, effects. + */ +class ControlUI extends OverlayUI { private final String[] patternNames; private final String[] transitionNames; private final String[] effectNames; - - private PImage logo; private int firstPatternY; private int firstPatternKnobY; @@ -45,48 +250,24 @@ class OverlayUI { private int firstTransitionKnobY; private int firstEffectY; private int firstEffectKnobY; + + private int autoRotateX; + private int autoRotateY; + + private final int PATTERN_LIST_LENGTH = 8; + private int patternScrollPos = 0; private int tempoY; private Method patternStateMethod; private Method transitionStateMethod; private Method effectStateMethod; - - private final int NUM_PATTERN_KNOBS = 8; - private final int NUM_TRANSITION_KNOBS = 4; - private final int NUM_EFFECT_KNOBS = 4; - - private int activeTransitionIndex = 0; - private int activeEffectIndex = 0; - - public final VirtualPatternKnob[] patternKnobs; - public final VirtualTransitionKnob[] transitionKnobs; - public final VirtualEffectKnob[] effectKnobs; - - OverlayUI() { - leftPos = width - w; - leftTextPos = leftPos + 4; - logo = loadImage("logo-sm.png"); - + + ControlUI() { patternNames = classNameArray(patterns, "Pattern"); transitionNames = classNameArray(transitions, "Transition"); effectNames = classNameArray(effects, "Effect"); - patternKnobs = new VirtualPatternKnob[NUM_PATTERN_KNOBS]; - for (int i = 0; i < patternKnobs.length; ++i) { - patternKnobs[i] = new VirtualPatternKnob(i); - } - - transitionKnobs = new VirtualTransitionKnob[NUM_TRANSITION_KNOBS]; - for (int i = 0; i < transitionKnobs.length; ++i) { - transitionKnobs[i] = new VirtualTransitionKnob(i); - } - - effectKnobs = new VirtualEffectKnob[NUM_EFFECT_KNOBS]; - for (int i = 0; i < effectKnobs.length; ++i) { - effectKnobs[i] = new VirtualEffectKnob(i); - } - try { patternStateMethod = getClass().getMethod("getState", LXPattern.class); effectStateMethod = getClass().getMethod("getState", LXEffect.class); @@ -95,30 +276,23 @@ class OverlayUI { throw new RuntimeException(x); } } - - void drawHelpTip() { - textFont(itemFont); - textAlign(RIGHT); - text("Tap 'u' to restore UI", width-4, height-6); - } - - void draw() { - image(logo, 4, 4); - - stroke(color(0, 0, 100)); - // fill(color(0, 0, 50, 50)); // alpha is bad for perf - fill(color(0, 0, 30)); - rect(leftPos-1, -1, w+2, height+2); - - int yPos = 0; + + public void draw() { + drawLogoAndBackground(); + int yPos = 0; + autoRotateX = leftPos + w - 29; + autoRotateY = yPos + 12; + drawToggleButton(autoRotateX, autoRotateY, lx.isAutoTransitionEnabled(), #999999); + fill(lx.isAutoTransitionEnabled() ? #222222: #999999); + text("A", autoRotateX + 2, autoRotateY + 9); firstPatternY = yPos + lineHeight + 6; - yPos = drawObjectList(yPos, "PATTERN", patterns, patternNames, patternStateMethod); + yPos = drawObjectList(yPos, "PATTERN", patterns, patternNames, patternStateMethod, PATTERN_LIST_LENGTH, patternScrollPos); yPos += controlSpacing; firstPatternKnobY = yPos; int xPos = leftTextPos; - for (int i = 0; i < NUM_PATTERN_KNOBS/2; ++i) { - drawKnob(xPos, yPos, knobSize, patternKnobs[i]); - drawKnob(xPos, yPos + knobSize + knobSpacing + knobLabelHeight, knobSize, patternKnobs[NUM_PATTERN_KNOBS/2 + i]); + for (int i = 0; i < glucose.NUM_PATTERN_KNOBS/2; ++i) { + drawKnob(xPos, yPos, knobSize, glucose.patternKnobs.get(i)); + drawKnob(xPos, yPos + knobSize + knobSpacing + knobLabelHeight, knobSize, glucose.patternKnobs.get(glucose.NUM_PATTERN_KNOBS/2 + i)); xPos += knobSize + knobSpacing; } yPos += 2*(knobSize + knobLabelHeight) + knobSpacing; @@ -129,8 +303,8 @@ class OverlayUI { yPos += controlSpacing; firstTransitionKnobY = yPos; xPos = leftTextPos; - for (int i = 0; i < transitionKnobs.length; ++i) { - drawKnob(xPos, yPos, knobSize, transitionKnobs[i]); + for (VirtualTransitionKnob knob : glucose.transitionKnobs) { + drawKnob(xPos, yPos, knobSize, knob); xPos += knobSize + knobSpacing; } yPos += knobSize + knobLabelHeight; @@ -141,8 +315,8 @@ class OverlayUI { yPos += controlSpacing; firstEffectKnobY = yPos; xPos = leftTextPos; - for (int i = 0; i < effectKnobs.length; ++i) { - drawKnob(xPos, yPos, knobSize, effectKnobs[i]); + for (VirtualEffectKnob knob : glucose.effectKnobs) { + drawKnob(xPos, yPos, knobSize, knob); xPos += knobSize + knobSpacing; } yPos += knobSize + knobLabelHeight; @@ -159,10 +333,7 @@ class OverlayUI { text("" + ((int)(lx.tempo.bpmf() * 100) / 100.), leftPos + w/2., yPos + tempoHeight - 6); yPos += tempoHeight; - fill(#999999); - textFont(itemFont); - textAlign(LEFT); - text("Tap 'u' to hide UI", leftTextPos, height-6); + drawToggleTip("Tap 'u' to hide"); } public LXParameter getOrNull(List items, int index) { @@ -172,17 +343,6 @@ class OverlayUI { return null; } - public void drawFPS() { - textFont(titleFont); - textAlign(LEFT); - fill(#666666); - text("FPS: " + (((int)(frameRate * 10)) / 10.), 4, height-6); - } - - private final int STATE_DEFAULT = 0; - private final int STATE_ACTIVE = 1; - private final int STATE_PENDING = 2; - public int getState(LXPattern p) { if (p == lx.getPattern()) { return STATE_ACTIVE; @@ -195,7 +355,7 @@ class OverlayUI { public int getState(LXEffect e) { if (e.isEnabled()) { return STATE_PENDING; - } else if (effects[activeEffectIndex] == e) { + } else if (e == glucose.getSelectedEffect()) { return STATE_ACTIVE; } return STATE_DEFAULT; @@ -204,61 +364,13 @@ class OverlayUI { public int getState(LXTransition t) { if (t == lx.getTransition()) { return STATE_PENDING; - } else if (t == transitions[activeTransitionIndex]) { + } else if (t == glucose.getSelectedTransition()) { return STATE_ACTIVE; } return STATE_DEFAULT; } - - protected int drawObjectList(int yPos, String title, Object[] items, Method stateMethod) { - return drawObjectList(yPos, title, items, classNameArray(items, null), stateMethod); - } - - private int drawObjectList(int yPos, String title, Object[] items, String[] names, Method stateMethod) { - noStroke(); - fill(#aaaaaa); - textFont(titleFont); - textAlign(LEFT); - text(title, leftTextPos, yPos += lineHeight); - if (items != null) { - textFont(itemFont); - color textColor; - boolean even = true; - for (int i = 0; i < items.length; ++i) { - Object o = items[i]; - int state = STATE_DEFAULT; - try { - state = ((Integer) stateMethod.invoke(this, o)).intValue(); - } catch (Exception x) { - throw new RuntimeException(x); - } - switch (state) { - case STATE_ACTIVE: - fill(lightGreen); - textColor = #eeeeee; - break; - case STATE_PENDING: - fill(lightBlue); - textColor = color(0, 0, 75 + 15*sin(millis()/200.));; - break; - default: - textColor = 0; - fill(even ? #666666 : #777777); - break; - } - rect(leftPos, yPos+6, width, lineHeight); - fill(textColor); - text(names[i], leftTextPos, yPos += lineHeight); - even = !even; - } - } - return yPos; - } private void drawKnob(int xPos, int yPos, int knobSize, LXParameter knob) { - if (!knobsOn) { - return; - } final float knobValue = knob.getValuef(); String knobLabel = knob.getLabel(); if (knobLabel == null) { @@ -298,86 +410,12 @@ class OverlayUI { textAlign(CENTER); textFont(knobFont); text(knobLabel, xPos + knobSize/2, yPos + knobSize + knobLabelHeight - 2); - - } - - private String[] classNameArray(Object[] objects, String suffix) { - if (objects == null) { - return null; - } - String[] names = new String[objects.length]; - for (int i = 0; i < objects.length; ++i) { - names[i] = className(objects[i], suffix); - } - return names; - } - - private String className(Object p, String suffix) { - String s = p.getClass().getName(); - int li; - if ((li = s.lastIndexOf(".")) > 0) { - s = s.substring(li + 1); - } - if (s.indexOf("SugarCubes$") == 0) { - s = s.substring("SugarCubes$".length()); - } - if ((suffix != null) && ((li = s.indexOf(suffix)) != -1)) { - s = s.substring(0, li); - } - return s; - } - - class VirtualPatternKnob extends LXVirtualParameter { - private final int index; - - VirtualPatternKnob(int index) { - this.index = index; - } - - public LXParameter getRealParameter() { - List parameters = glucose.getPattern().getParameters(); - if (index < parameters.size()) { - return parameters.get(index); - } - return null; - } - } - - class VirtualTransitionKnob extends LXVirtualParameter { - private final int index; - - VirtualTransitionKnob(int index) { - this.index = index; - } - - public LXParameter getRealParameter() { - List parameters = transitions[activeTransitionIndex].getParameters(); - if (index < parameters.size()) { - return parameters.get(index); - } - return null; - } - } - - class VirtualEffectKnob extends LXVirtualParameter { - private final int index; - - VirtualEffectKnob(int index) { - this.index = index; - } - - public LXParameter getRealParameter() { - List parameters = effects[activeEffectIndex].getParameters(); - if (index < parameters.size()) { - return parameters.get(index); - } - return null; - } } private int patternKnobIndex = -1; private int transitionKnobIndex = -1; private int effectKnobIndex = -1; + private boolean patternScrolling = false; private int lastY; private int releaseEffect = -1; @@ -387,6 +425,36 @@ class OverlayUI { lastY = mouseY; patternKnobIndex = transitionKnobIndex = effectKnobIndex = -1; releaseEffect = -1; + patternScrolling = false; + + for (int p = 0; p < pandaLeft.length; ++p) { + int xp = pandaLeft[p]; + if ((mouseX >= xp) && + (mouseX < xp + pandaWidth) && + (mouseY >= pandaTop) && + (mouseY < pandaTop + pandaHeight)) { + pandaBoards[p].toggle(); + } + } + + if (mouseX < leftPos) { + return; + } + + if ((mouseX >= autoRotateX) && + (mouseX < autoRotateX + toggleButtonSize) && + (mouseY >= autoRotateY) && + (mouseY < autoRotateY + toggleButtonSize)) { + if (lx.isAutoTransitionEnabled()) { + lx.disableAutoTransition(); + println("Auto pattern transition disabled"); + } else { + lx.enableAutoTransition(60000); + println("Auto pattern transition enabled"); + } + return; + } + if (mouseY > tempoY) { if (mouseY - tempoY < tempoHeight) { lx.tempo.tap(); @@ -395,51 +463,65 @@ class OverlayUI { } else if ((mouseY >= firstEffectKnobY) && (mouseY < firstEffectKnobY + knobSize + knobLabelHeight)) { effectKnobIndex = (mouseX - leftTextPos) / (knobSize + knobSpacing); } else if (mouseY > firstEffectY) { - int effectIndex = (mouseY - firstEffectY) / lineHeight; + int effectIndex = objectClickIndex(firstEffectY); if (effectIndex < effects.length) { - if (activeEffectIndex == effectIndex) { + if (effects[effectIndex] == glucose.getSelectedEffect()) { effects[effectIndex].enable(); releaseEffect = effectIndex; } - activeEffectIndex = effectIndex; + glucose.setSelectedEffect(effectIndex); } } else if ((mouseY >= firstTransitionKnobY) && (mouseY < firstTransitionKnobY + knobSize + knobLabelHeight)) { transitionKnobIndex = (mouseX - leftTextPos) / (knobSize + knobSpacing); } else if (mouseY > firstTransitionY) { - int transitionIndex = (mouseY - firstTransitionY) / lineHeight; + int transitionIndex = objectClickIndex(firstTransitionY); if (transitionIndex < transitions.length) { - activeTransitionIndex = transitionIndex; + glucose.setSelectedTransition(transitionIndex); } } else if ((mouseY >= firstPatternKnobY) && (mouseY < firstPatternKnobY + 2*(knobSize+knobLabelHeight) + knobSpacing)) { patternKnobIndex = (mouseX - leftTextPos) / (knobSize + knobSpacing); if (mouseY >= firstPatternKnobY + knobSize + knobLabelHeight + knobSpacing) { - patternKnobIndex += NUM_PATTERN_KNOBS / 2; + patternKnobIndex += glucose.NUM_PATTERN_KNOBS / 2; } } else if (mouseY > firstPatternY) { - int patternIndex = (mouseY - firstPatternY) / lineHeight; - if (patternIndex < patterns.length) { - patterns[patternIndex].setTransition(transitions[activeTransitionIndex]); - lx.goIndex(patternIndex); + if ((patterns.length > PATTERN_LIST_LENGTH) && (mouseX > width - scrollWidth)) { + patternScrolling = true; + } else { + int patternIndex = objectClickIndex(firstPatternY); + if (patternIndex < patterns.length) { + if (lx.isAutoTransitionEnabled() && (mouseX > eligibleLeft)) { + patterns[patternIndex + patternScrollPos].toggleEligible(); + } else { + lx.goIndex(patternIndex + patternScrollPos); + } + } } } } + int scrolldy = 0; public void mouseDragged() { int dy = lastY - mouseY; + scrolldy += dy; lastY = mouseY; - if (patternKnobIndex >= 0 && patternKnobIndex < NUM_PATTERN_KNOBS) { - LXParameter p = patternKnobs[patternKnobIndex]; + if (patternKnobIndex >= 0 && patternKnobIndex < glucose.NUM_PATTERN_KNOBS) { + LXParameter p = glucose.patternKnobs.get(patternKnobIndex); p.setValue(constrain(p.getValuef() + dy*.01, 0, 1)); - } else if (effectKnobIndex >= 0 && effectKnobIndex < NUM_EFFECT_KNOBS) { - LXParameter p = effectKnobs[effectKnobIndex]; + } else if (effectKnobIndex >= 0 && effectKnobIndex < glucose.NUM_EFFECT_KNOBS) { + LXParameter p = glucose.effectKnobs.get(effectKnobIndex); p.setValue(constrain(p.getValuef() + dy*.01, 0, 1)); - } else if (transitionKnobIndex >= 0 && transitionKnobIndex < NUM_TRANSITION_KNOBS) { - LXParameter p = transitionKnobs[transitionKnobIndex]; + } else if (transitionKnobIndex >= 0 && transitionKnobIndex < glucose.NUM_TRANSITION_KNOBS) { + LXParameter p = glucose.transitionKnobs.get(transitionKnobIndex); p.setValue(constrain(p.getValuef() + dy*.01, 0, 1)); + } else if (patternScrolling) { + int scroll = scrolldy / lineHeight; + scrolldy = scrolldy % lineHeight; + patternScrollPos = constrain(patternScrollPos - scroll, 0, patterns.length - PATTERN_LIST_LENGTH); } } public void mouseReleased() { + patternScrolling = false; tempoDown = false; if (releaseEffect >= 0) { effects[releaseEffect].trigger(); @@ -447,23 +529,450 @@ class OverlayUI { } } + public void mouseWheel(int delta) { + if (mouseY > firstPatternY) { + int patternIndex = objectClickIndex(firstPatternY); + if (patternIndex < PATTERN_LIST_LENGTH) { + patternScrollPos = constrain(patternScrollPos + delta, 0, patterns.length - PATTERN_LIST_LENGTH); + } + } + } + } -void mousePressed() { - if (mouseX > ui.leftPos) { - ui.mousePressed(); +/** + * UI for control of mapping. + */ +class MappingUI extends OverlayUI { + + private MappingTool mappingTool; + + private final String MAPPING_MODE_ALL = "All On"; + private final String MAPPING_MODE_CHANNEL = "Channel"; + private final String MAPPING_MODE_SINGLE_CUBE = "Single Cube"; + + private final String[] mappingModes = { + MAPPING_MODE_ALL, + MAPPING_MODE_CHANNEL, + MAPPING_MODE_SINGLE_CUBE + }; + private final Method mappingModeStateMethod; + + private final String CUBE_MODE_ALL = "All Strips"; + private final String CUBE_MODE_SINGLE_STRIP = "Single Strip"; + private final String CUBE_MODE_STRIP_PATTERN = "Strip Pattern"; + private final String[] cubeModes = { + CUBE_MODE_ALL, + CUBE_MODE_SINGLE_STRIP, + CUBE_MODE_STRIP_PATTERN + }; + private final Method cubeModeStateMethod; + + private final String CHANNEL_MODE_RED = "Red"; + private final String CHANNEL_MODE_GREEN = "Green"; + private final String CHANNEL_MODE_BLUE = "Blue"; + private final String[] channelModes = { + CHANNEL_MODE_RED, + CHANNEL_MODE_GREEN, + CHANNEL_MODE_BLUE, + }; + private final Method channelModeStateMethod; + + private int firstMappingY; + private int firstCubeY; + private int firstChannelY; + private int channelFieldY; + private int cubeFieldY; + private int stripFieldY; + + private boolean dragCube; + private boolean dragStrip; + private boolean dragChannel; + + MappingUI(MappingTool mappingTool) { + this.mappingTool = mappingTool; + try { + mappingModeStateMethod = getClass().getMethod("getMappingState", Object.class); + channelModeStateMethod = getClass().getMethod("getChannelState", Object.class); + cubeModeStateMethod = getClass().getMethod("getCubeState", Object.class); + } catch (Exception x) { + throw new RuntimeException(x); + } + } + + public int getMappingState(Object mappingMode) { + boolean active = false; + if (mappingMode == MAPPING_MODE_ALL) { + active = mappingTool.mappingMode == mappingTool.MAPPING_MODE_ALL; + } else if (mappingMode == MAPPING_MODE_CHANNEL) { + active = mappingTool.mappingMode == mappingTool.MAPPING_MODE_CHANNEL; + } else if (mappingMode == MAPPING_MODE_SINGLE_CUBE) { + active = mappingTool.mappingMode == mappingTool.MAPPING_MODE_SINGLE_CUBE; + } + return active ? STATE_ACTIVE : STATE_DEFAULT; + } + + public int getChannelState(Object channelMode) { + boolean active = false; + if (channelMode == CHANNEL_MODE_RED) { + active = mappingTool.channelModeRed; + } else if (channelMode == CHANNEL_MODE_GREEN) { + active = mappingTool.channelModeGreen; + } else if (channelMode == CHANNEL_MODE_BLUE) { + active = mappingTool.channelModeBlue; + } + return active ? STATE_ACTIVE : STATE_DEFAULT; + } + + public int getCubeState(Object cubeMode) { + boolean active = false; + if (cubeMode == CUBE_MODE_ALL) { + active = mappingTool.cubeMode == mappingTool.CUBE_MODE_ALL; + } else if (cubeMode == CUBE_MODE_SINGLE_STRIP) { + active = mappingTool.cubeMode == mappingTool.CUBE_MODE_SINGLE_STRIP; + } else if (cubeMode == CUBE_MODE_STRIP_PATTERN) { + active = mappingTool.cubeMode == mappingTool.CUBE_MODE_STRIP_PATTERN; + } + return active ? STATE_ACTIVE : STATE_DEFAULT; + } + + public void draw() { + drawLogoAndBackground(); + + int yPos = 0; + firstMappingY = yPos + lineHeight + 6; + yPos = drawObjectList(yPos, "MAPPING MODE", mappingModes, mappingModes, mappingModeStateMethod); + yPos += sectionSpacing; + + firstCubeY = yPos + lineHeight + 6; + yPos = drawObjectList(yPos, "CUBE MODE", cubeModes, cubeModes, cubeModeStateMethod); + yPos += sectionSpacing; + + firstChannelY = yPos + lineHeight + 6; + yPos = drawObjectList(yPos, "CHANNELS", channelModes, channelModes, channelModeStateMethod); + yPos += sectionSpacing; + + channelFieldY = yPos + lineHeight + 6; + yPos = drawValueField(yPos, "CHANNEL ID", mappingTool.channelIndex + 1); + yPos += sectionSpacing; + + cubeFieldY = yPos + lineHeight + 6; + yPos = drawValueField(yPos, "CUBE ID", glucose.model.getRawIndexForCube(mappingTool.cubeIndex)); + yPos += sectionSpacing; + + stripFieldY = yPos + lineHeight + 6; + yPos = drawValueField(yPos, "STRIP ID", mappingTool.stripIndex + 1); + + drawToggleTip("Tap 'm' to return"); } -} + + private int drawValueField(int yPos, String label, int value) { + yPos += lineHeight; + textAlign(LEFT); + textFont(titleFont); + fill(titleColor); + text(label, leftTextPos, yPos); + fill(0); + yPos += 6; + rect(leftTextPos, yPos, w-8, lineHeight); + yPos += lineHeight; -void mouseReleased() { - if (mouseX > ui.leftPos) { - ui.mouseReleased(); + fill(#999999); + textAlign(CENTER); + textFont(itemFont); + text("" + value, leftTextPos + (w-8)/2, yPos - 5); + + return yPos; } -} -void mouseDragged() { - if (mouseX > ui.leftPos) { - ui.mouseDragged(); + private int lastY; + + public void mousePressed() { + dragCube = dragStrip = dragChannel = false; + lastY = mouseY; + + if (mouseX < leftPos) { + return; + } + + if (mouseY >= stripFieldY) { + if (mouseY < stripFieldY + lineHeight) { + dragStrip = true; + } + } else if (mouseY >= cubeFieldY) { + if (mouseY < cubeFieldY + lineHeight) { + dragCube = true; + } + } else if (mouseY >= channelFieldY) { + if (mouseY < channelFieldY + lineHeight) { + dragChannel = true; + } + } else if (mouseY >= firstChannelY) { + int index = objectClickIndex(firstChannelY); + switch (index) { + case 0: mappingTool.channelModeRed = !mappingTool.channelModeRed; break; + case 1: mappingTool.channelModeGreen = !mappingTool.channelModeGreen; break; + case 2: mappingTool.channelModeBlue = !mappingTool.channelModeBlue; break; + } + } else if (mouseY >= firstCubeY) { + int index = objectClickIndex(firstCubeY); + switch (index) { + case 0: mappingTool.cubeMode = mappingTool.CUBE_MODE_ALL; break; + case 1: mappingTool.cubeMode = mappingTool.CUBE_MODE_SINGLE_STRIP; break; + case 2: mappingTool.cubeMode = mappingTool.CUBE_MODE_STRIP_PATTERN; break; + } + } else if (mouseY >= firstMappingY) { + int index = objectClickIndex(firstMappingY); + switch (index) { + case 0: mappingTool.mappingMode = mappingTool.MAPPING_MODE_ALL; break; + case 1: mappingTool.mappingMode = mappingTool.MAPPING_MODE_CHANNEL; break; + case 2: mappingTool.mappingMode = mappingTool.MAPPING_MODE_SINGLE_CUBE; break; + } + } + } + + public void mouseReleased() {} + public void mouseWheel(int delta) {} + + public void mouseDragged() { + final int DRAG_THRESHOLD = 5; + int dy = lastY - mouseY; + if (abs(dy) >= DRAG_THRESHOLD) { + lastY = mouseY; + if (dragCube) { + if (dy < 0) { + mappingTool.decCube(); + } else { + mappingTool.incCube(); + } + } else if (dragStrip) { + if (dy < 0) { + mappingTool.decStrip(); + } else { + mappingTool.incStrip(); + } + } else if (dragChannel) { + if (dy < 0) { + mappingTool.decChannel(); + } else { + mappingTool.incChannel(); + } + } + } + } } +class DebugUI { + + final ChannelMapping[] channelList; + final int debugX = 5; + final int debugY = 5; + final int debugXSpacing = 28; + final int debugYSpacing = 21; + final int[][] debugState; + + final int DEBUG_STATE_ANIM = 0; + final int DEBUG_STATE_WHITE = 1; + final int DEBUG_STATE_OFF = 2; + + DebugUI(PandaMapping[] pandaMappings) { + int totalChannels = pandaMappings.length * PandaMapping.CHANNELS_PER_BOARD; + debugState = new int[totalChannels+1][ChannelMapping.CUBES_PER_CHANNEL+1]; + + channelList = new ChannelMapping[totalChannels]; + int channelIndex = 0; + for (PandaMapping pm : pandaMappings) { + for (ChannelMapping channel : pm.channelList) { + channelList[channelIndex++] = channel; + } + } + for (int i = 0; i < debugState.length; ++i) { + for (int j = 0; j < debugState[i].length; ++j) { + debugState[i][j] = DEBUG_STATE_ANIM; + } + } + } + + void draw() { + noStroke(); + int xBase = debugX; + int yPos = debugY; + + fill(#000000); + rect(0, 0, debugX + 5*debugXSpacing, height); + + int channelNum = 0; + for (ChannelMapping channel : channelList) { + int xPos = xBase; + drawNumBox(xPos, yPos, channelNum+1, debugState[channelNum][0]); + xPos += debugXSpacing; + + switch (channel.mode) { + case ChannelMapping.MODE_CUBES: + int stateIndex = 0; + boolean first = true; + for (int rawCubeIndex : channel.objectIndices) { + if (rawCubeIndex < 0) { + break; + } + if (first) { + first = false; + } else { + stroke(#999999); + line(xPos - 12, yPos + 8, xPos, yPos + 8); + } + drawNumBox(xPos, yPos, rawCubeIndex, debugState[channelNum][stateIndex+1]); + ++stateIndex; + xPos += debugXSpacing; + } + break; + case ChannelMapping.MODE_BASS: + drawNumBox(xPos, yPos, "B", debugState[channelNum][1]); + break; + case ChannelMapping.MODE_SPEAKER: + drawNumBox(xPos, yPos, "S" + channel.objectIndices[0], debugState[channelNum][1]); + break; + case ChannelMapping.MODE_STRUTS_AND_FLOOR: + drawNumBox(xPos, yPos, "F", debugState[channelNum][1]); + break; + case ChannelMapping.MODE_NULL: + break; + default: + throw new RuntimeException("Unhandled channel mapping mode: " + channel.mode); + } + + yPos += debugYSpacing; + ++channelNum; + } + drawNumBox(xBase, yPos, "A", debugState[channelNum][0]); + } + + void drawNumBox(int xPos, int yPos, int label, int state) { + drawNumBox(xPos, yPos, "" + label, state); + } + + void drawNumBox(int xPos, int yPos, String label, int state) { + noFill(); + color textColor = #cccccc; + switch (state) { + case DEBUG_STATE_ANIM: + noStroke(); + fill(#880000); + rect(xPos, yPos, 16, 8); + fill(#000088); + rect(xPos, yPos+8, 16, 8); + noFill(); + stroke(textColor); + rect(xPos, yPos, 16, 16); + break; + case DEBUG_STATE_WHITE: + stroke(textColor); + fill(#e9e9e9); + rect(xPos, yPos, 16, 16); + textColor = #333333; + break; + case DEBUG_STATE_OFF: + stroke(textColor); + rect(xPos, yPos, 16, 16); + break; + } + + noStroke(); + fill(textColor); + text(label, xPos + 2, yPos + 12); + + } + + void maskColors(color[] colors) { + color white = #FFFFFF; + color off = #000000; + int channelIndex = 0; + int state; + for (ChannelMapping channel : channelList) { + switch (channel.mode) { + case ChannelMapping.MODE_CUBES: + int cubeIndex = 1; + for (int rawCubeIndex : channel.objectIndices) { + if (rawCubeIndex >= 0) { + state = debugState[channelIndex][cubeIndex]; + if (state != DEBUG_STATE_ANIM) { + color debugColor = (state == DEBUG_STATE_WHITE) ? white : off; + Cube cube = glucose.model.getCubeByRawIndex(rawCubeIndex); + for (Point p : cube.points) { + colors[p.index] = debugColor; + } + } + } + ++cubeIndex; + } + break; + + case ChannelMapping.MODE_BASS: + state = debugState[channelIndex][1]; + if (state != DEBUG_STATE_ANIM) { + color debugColor = (state == DEBUG_STATE_WHITE) ? white : off; + for (Strip s : glucose.model.bassBox.boxStrips) { + for (Point p : s.points) { + colors[p.index] = debugColor; + } + } + } + break; + + case ChannelMapping.MODE_STRUTS_AND_FLOOR: + state = debugState[channelIndex][1]; + if (state != DEBUG_STATE_ANIM) { + color debugColor = (state == DEBUG_STATE_WHITE) ? white : off; + for (Point p : glucose.model.boothFloor.points) { + colors[p.index] = debugColor; + } + for (Strip s : glucose.model.bassBox.struts) { + for (Point p : s.points) { + colors[p.index] = debugColor; + } + } + } + break; + + case ChannelMapping.MODE_SPEAKER: + state = debugState[channelIndex][1]; + if (state != DEBUG_STATE_ANIM) { + color debugColor = (state == DEBUG_STATE_WHITE) ? white : off; + for (Point p : glucose.model.speakers.get(channel.objectIndices[0]).points) { + colors[p.index] = debugColor; + } + } + break; + + case ChannelMapping.MODE_NULL: + break; + + default: + throw new RuntimeException("Unhandled channel mapping mode: " + channel.mode); + } + ++channelIndex; + } + } + + void mousePressed() { + int dx = (mouseX - debugX) / debugXSpacing; + int dy = (mouseY - debugY) / debugYSpacing; + if ((dy >= 0) && (dy < debugState.length)) { + if ((dx >= 0) && (dx < debugState[dy].length)) { + int newState = debugState[dy][dx] = (debugState[dy][dx] + 1) % 3; + if (dy == debugState.length-1) { + for (int[] states : debugState) { + for (int i = 0; i < states.length; ++i) { + states[i] = newState; + } + } + } else if (dx == 0) { + for (int i = 0; i < debugState[dy].length; ++i) { + debugState[dy][i] = newState; + } + } + } + } + } +}