import heronarts.lx.pattern.*;
import heronarts.lx.transform.*;
import heronarts.lx.transition.*;
+import heronarts.lx.ui.*;
+import heronarts.lx.ui.component.*;
+import heronarts.lx.ui.control.*;
import ddf.minim.*;
import ddf.minim.analysis.*;
import processing.opengl.*;
float[] hsb = new float[3];
// Handles to UI objects
-UIContext[] overlays;
UIPatternDeck uiPatternA;
UICrossfader uiCrossfader;
UIMidi uiMidi;
UIDebugText uiDebugText;
UISpeed uiSpeed;
-// Camera variables
-float eyeR, eyeA, eyeX, eyeY, eyeZ, midX, midY, midZ;
-
/**
* Engine construction and initialization.
*/
// Build overlay UI
debugUI = new DebugUI(pandaMappings);
- overlays = new UIContext[] {
- uiPatternA = new UIPatternDeck(lx.engine.getDeck(GLucose.LEFT_DECK), "PATTERN A", 4, 4, 140, 324),
+ UIContext[] contexts = new UIContext[] {
+ uiPatternA = new UIPatternDeck(lx.ui, lx.engine.getDeck(GLucose.LEFT_DECK), "PATTERN A", 4, 4, 140, 324),
new UIBlendMode(4, 332, 140, 86),
new UIEffects(4, 422, 140, 144),
new UITempo(4, 570, 140, 50),
uiSpeed = new UISpeed(4, 624, 140, 50),
- new UIPatternDeck(lx.engine.getDeck(GLucose.RIGHT_DECK), "PATTERN B", width-144, 4, 140, 324),
+ new UIPatternDeck(lx.ui, lx.engine.getDeck(GLucose.RIGHT_DECK), "PATTERN B", width-144, 4, 140, 324),
uiMidi = new UIMidi(midiEngine, width-144, 332, 140, 158),
new UIOutput(width-144, 494, 140, 106),
uiCrossfader = new UICrossfader(width/2-90, height-90, 180, 86),
uiDebugText = new UIDebugText(148, height-138, width-304, 44),
- uiMapping = new UIMapping(mappingTool, 4, 4, 140, 324),
+ uiMapping = new UIMapping(mappingTool, 4, 4, 140, 324)
};
uiMapping.setVisible(false);
+ lx.ui.addLayer(new UICameraLayer(lx.ui).setCenter(TRAILER_WIDTH/2., glucose.model.yMax/2, TRAILER_DEPTH/2.).setRadius(290).addComponent(new UICubesLayer()));
+ for (UIContext context : contexts) {
+ lx.ui.addLayer(context);
+ }
logTime("Built overlay UI");
// Load logo image
logo = loadImage("data/logo.png");
-
- // Setup camera
- midX = TRAILER_WIDTH/2.;
- midY = glucose.model.yMax/2;
- midZ = TRAILER_DEPTH/2.;
- eyeR = -290;
- eyeA = .15;
- eyeY = midY + 70;
- eyeX = midX + eyeR*sin(eyeA);
- eyeZ = midZ + eyeR*cos(eyeA);
-
- // Add mouse scrolling event support
- addMouseWheelListener(new java.awt.event.MouseWheelListener() {
- public void mouseWheelMoved(java.awt.event.MouseWheelEvent mwe) {
- mouseWheel(mwe.getWheelRotation());
- }});
-
+
println("Total setup: " + (millis() - startMillis) + "ms");
println("Hit the 'p' key to toggle Panda Board output");
}
void draw() {
long drawStart = System.nanoTime();
- // Draws the simulation and the 2D UI overlay
+ // Set background
background(40);
-
- color[] simulationColors;
- color[] sendColors;
- simulationColors = sendColors = glucose.getColors();
- String displayMode = uiCrossfader.getDisplayMode();
- if (displayMode == "A") {
- simulationColors = lx.engine.getDeck(GLucose.LEFT_DECK).getColors();
- } else if (displayMode == "B") {
- simulationColors = lx.engine.getDeck(GLucose.RIGHT_DECK).getColors();
- }
+
+ // Send colors
+ color[] sendColors = glucose.getColors();
if (debugMode) {
- debugUI.maskColors(simulationColors);
debugUI.maskColors(sendColors);
}
-
- long simulationStart = System.nanoTime();
- if (simulationOn) {
- drawSimulation(simulationColors);
- }
- long simulationNanos = System.nanoTime() - simulationStart;
-
- // 2D Overlay UI
- long uiStart = System.nanoTime();
- drawUI();
- long uiNanos = System.nanoTime() - uiStart;
long gammaStart = System.nanoTime();
// Gamma correction here. Apply a cubic to the brightness
sendColors[i] = lx.hsb(360.*hsb[0], 100.*hsb[1], 100.*(b*b*b));
}
long gammaNanos = System.nanoTime() - gammaStart;
-
+
long sendStart = System.nanoTime();
for (PandaDriver p : pandaBoards) {
p.send(sendColors);
}
long sendNanos = System.nanoTime() - sendStart;
-
+
+ drawFPS();
+ if (debugMode) {
+ debugUI.draw();
+ }
+
+ // TODO(mcslee): fix
long drawNanos = System.nanoTime() - drawStart;
+ long simulationNanos = 0, uiNanos = 0;
if (diagnosticsOn) {
drawDiagnostics(drawNanos, simulationNanos, uiNanos, gammaNanos, sendNanos);
- }
+ }
+}
+
+class UICubesLayer extends UICameraComponent {
+ void onDraw(UI ui) {
+ color[] simulationColors = glucose.getColors();
+ String displayMode = uiCrossfader.getDisplayMode();
+ if (displayMode == "A") {
+ simulationColors = lx.engine.getDeck(GLucose.LEFT_DECK).getColors();
+ } else if (displayMode == "B") {
+ simulationColors = lx.engine.getDeck(GLucose.RIGHT_DECK).getColors();
+ }
+ if (debugMode) {
+ debugUI.maskColors(simulationColors);
+ }
+
+ long simulationStart = System.nanoTime();
+ if (simulationOn) {
+ drawSimulation(simulationColors);
+ }
+ long simulationNanos = System.nanoTime() - simulationStart;
+
+ camera();
+ javax.media.opengl.GL gl = ((PGraphicsOpenGL)g).beginGL();
+ gl.glClear(javax.media.opengl.GL.GL_DEPTH_BUFFER_BIT);
+ ((PGraphicsOpenGL)g).endGL();
+ strokeWeight(1);
+ }
}
void drawDiagnostics(long drawNanos, long simulationNanos, long uiNanos, long gammaNanos, long sendNanos) {
}
void drawSimulation(color[] simulationColors) {
- camera(
- eyeX, eyeY, eyeZ,
- midX, midY, midZ,
- 0, -1, 0
- );
-
translate(0, 40, 0);
noStroke();
popMatrix();
}
-void drawUI() {
- camera();
- javax.media.opengl.GL gl = ((PGraphicsOpenGL)g).beginGL();
- gl.glClear(javax.media.opengl.GL.GL_DEPTH_BUFFER_BIT);
- ((PGraphicsOpenGL)g).endGL();
- strokeWeight(1);
-
- if (uiOn) {
- for (UIContext context : overlays) {
- context.draw();
- }
- }
-
+void drawFPS() {
// Always draw FPS meter
fill(#555555);
textSize(9);
textAlign(LEFT, BASELINE);
text("FPS: " + ((int) (frameRate*10)) / 10. + " / " + targetFramerate + " (-/+)", 4, height-4);
-
- if (debugMode) {
- debugUI.draw();
- }
}
/**
* Top-level mouse event handling
*/
-int mx, my;
void mousePressed() {
- boolean debugged = false;
if (debugMode) {
- debugged = debugUI.mousePressed();
- }
- if (!debugged) {
- for (UIContext context : overlays) {
- context.mousePressed(mouseX, mouseY);
- }
- }
- mx = mouseX;
- my = mouseY;
-}
-
-void mouseDragged() {
- boolean dragged = false;
- for (UIContext context : overlays) {
- dragged |= context.mouseDragged(mouseX, mouseY);
- }
- if (!dragged) {
- int dx = mouseX - mx;
- int dy = mouseY - my;
- mx = mouseX;
- my = mouseY;
- eyeA += dx*.003;
- eyeX = midX + eyeR*sin(eyeA);
- eyeZ = midZ + eyeR*cos(eyeA);
- eyeY += dy;
- }
-}
-
-void mouseReleased() {
- for (UIContext context : overlays) {
- context.mouseReleased(mouseX, mouseY);
+ debugUI.mousePressed();
}
}
-void mouseWheel(int delta) {
- boolean wheeled = false;
- for (UIContext context : overlays) {
- wheeled |= context.mouseWheel(mouseX, mouseY, delta);
- }
-
- if (!wheeled) {
- eyeR = constrain(eyeR - delta, -500, -80);
- eyeX = midX + eyeR*sin(eyeA);
- eyeZ = midZ + eyeR*cos(eyeA);
- }
-}
*
* Custom UI components using the framework.
*/
-
-class UIPatternDeck extends UIWindow {
-
- LXDeck deck;
-
- public UIPatternDeck(LXDeck deck, String label, float x, float y, float w, float h) {
- super(label, x, y, w, h);
- this.deck = deck;
- int yp = titleHeight;
-
- List<ScrollItem> items = new ArrayList<ScrollItem>();
- for (LXPattern p : deck.getPatterns()) {
- items.add(new PatternScrollItem(p));
- }
- final UIScrollList patternList = new UIScrollList(1, yp, w-2, 140).setItems(items);
- patternList.addToContainer(this);
- yp += patternList.h + 10;
-
- final UIParameterKnob[] parameterKnobs = new UIParameterKnob[12];
- for (int ki = 0; ki < parameterKnobs.length; ++ki) {
- parameterKnobs[ki] = new UIParameterKnob(5 + 34*(ki % 4), yp + (ki/4) * 48);
- parameterKnobs[ki].addToContainer(this);
- }
-
- LXDeck.Listener lxListener = new LXDeck.AbstractListener() {
- public void patternWillChange(LXDeck deck, LXPattern pattern, LXPattern nextPattern) {
- patternList.redraw();
- }
- public void patternDidChange(LXDeck deck, LXPattern pattern) {
- patternList.redraw();
- int pi = 0;
- for (LXParameter parameter : pattern.getParameters()) {
- if (pi >= parameterKnobs.length) {
- break;
- }
- parameterKnobs[pi++].setParameter(parameter);
- }
- while (pi < parameterKnobs.length) {
- parameterKnobs[pi++].setParameter(null);
- }
- }
- };
-
- deck.addListener(lxListener);
- lxListener.patternDidChange(deck, deck.getActivePattern());
-
- }
-
- class PatternScrollItem extends AbstractScrollItem {
-
- private LXPattern pattern;
- private String label;
-
- PatternScrollItem(LXPattern pattern) {
- this.pattern = pattern;
- label = className(pattern, "Pattern");
- }
-
- public String getLabel() {
- return label;
- }
-
- public boolean isSelected() {
- return deck.getActivePattern() == pattern;
- }
-
- public boolean isPending() {
- return deck.getNextPattern() == pattern;
- }
-
- public void onMousePressed() {
- deck.goPattern(pattern);
- }
- }
-}
class UIBlendMode extends UIWindow {
public UIBlendMode(float x, float y, float w, float h) {
- super("BLEND MODE", x, y, w, h);
- List<ScrollItem> items = new ArrayList<ScrollItem>();
+ super(lx.ui, "BLEND MODE", x, y, w, h);
+ List<UIScrollList.Item> items = new ArrayList<UIScrollList.Item>();
for (LXTransition t : glucose.getTransitions()) {
items.add(new TransitionScrollItem(t));
}
final UIScrollList tList;
- (tList = new UIScrollList(1, titleHeight, w-2, 60)).setItems(items).addToContainer(this);
+ (tList = new UIScrollList(1, UIWindow.TITLE_LABEL_HEIGHT, w-2, 60)).setItems(items).addToContainer(this);
lx.engine.getDeck(GLucose.RIGHT_DECK).addListener(new LXDeck.AbstractListener() {
public void faderTransitionDidChange(LXDeck deck, LXTransition transition) {
});
}
- class TransitionScrollItem extends AbstractScrollItem {
+ class TransitionScrollItem extends UIScrollList.AbstractItem {
private final LXTransition transition;
private String label;
private final UIToggleSet displayMode;
public UICrossfader(float x, float y, float w, float h) {
- super("CROSSFADER", x, y, w, h);
+ super(lx.ui, "CROSSFADER", x, y, w, h);
- new UIParameterSlider(4, titleHeight, w-9, 32).setParameter(lx.engine.getDeck(GLucose.RIGHT_DECK).getFader()).addToContainer(this);
- (displayMode = new UIToggleSet(4, titleHeight + 36, w-9, 20)).setOptions(new String[] { "A", "COMP", "B" }).setValue("COMP").addToContainer(this);
+ new UISlider(4, UIWindow.TITLE_LABEL_HEIGHT, w-9, 32).setParameter(lx.engine.getDeck(GLucose.RIGHT_DECK).getFader()).addToContainer(this);
+ (displayMode = new UIToggleSet(4, UIWindow.TITLE_LABEL_HEIGHT + 36, w-9, 20)).setOptions(new String[] { "A", "COMP", "B" }).setValue("COMP").addToContainer(this);
}
public UICrossfader setDisplayMode(String value) {
class UIEffects extends UIWindow {
UIEffects(float x, float y, float w, float h) {
- super("FX", x, y, w, h);
+ super(lx.ui, "FX", x, y, w, h);
- int yp = titleHeight;
- List<ScrollItem> items = new ArrayList<ScrollItem>();
+ int yp = UIWindow.TITLE_LABEL_HEIGHT;
+ List<UIScrollList.Item> items = new ArrayList<UIScrollList.Item>();
for (LXEffect fx : glucose.lx.getEffects()) {
items.add(new FXScrollItem(fx));
}
final UIScrollList effectsList = new UIScrollList(1, yp, w-2, 60).setItems(items);
effectsList.addToContainer(this);
- yp += effectsList.h + 10;
+ yp += effectsList.getHeight() + 10;
- final UIParameterKnob[] parameterKnobs = new UIParameterKnob[4];
+ final UIKnob[] parameterKnobs = new UIKnob[4];
for (int ki = 0; ki < parameterKnobs.length; ++ki) {
- parameterKnobs[ki] = new UIParameterKnob(5 + 34*(ki % 4), yp + (ki/4) * 48);
+ parameterKnobs[ki] = new UIKnob(5 + 34*(ki % 4), yp + (ki/4) * 48);
parameterKnobs[ki].addToContainer(this);
}
if (i >= parameterKnobs.length) {
break;
}
- parameterKnobs[i++].setParameter(p);
+ if (p instanceof BasicParameter) {
+ parameterKnobs[i++].setParameter((BasicParameter) p);
+ }
}
while (i < parameterKnobs.length) {
parameterKnobs[i++].setParameter(null);
}
- class FXScrollItem extends AbstractScrollItem {
+ class FXScrollItem extends UIScrollList.AbstractItem {
private LXEffect effect;
private String label;
class UIOutput extends UIWindow {
public UIOutput(float x, float y, float w, float h) {
- super("OUTPUT", x, y, w, h);
- float yp = titleHeight;
+ super(lx.ui, "OUTPUT", x, y, w, h);
+ float yp = UIWindow.TITLE_LABEL_HEIGHT;
- final UIScrollList outputs = new UIScrollList(1, titleHeight, w-2, 80);
+ final UIScrollList outputs = new UIScrollList(1, UIWindow.TITLE_LABEL_HEIGHT, w-2, 80);
- List<ScrollItem> items = new ArrayList<ScrollItem>();
+ List<UIScrollList.Item> items = new ArrayList<UIScrollList.Item>();
for (final PandaDriver panda : pandaBoards) {
items.add(new PandaScrollItem(panda));
panda.setListener(new PandaDriver.Listener() {
outputs.setItems(items).addToContainer(this);
}
- class PandaScrollItem extends AbstractScrollItem {
+ class PandaScrollItem extends UIScrollList.AbstractItem {
final PandaDriver panda;
PandaScrollItem(PandaDriver panda) {
this.panda = panda;
private final UIButton tempoButton;
UITempo(float x, float y, float w, float h) {
- super("TEMPO", x, y, w, h);
- tempoButton = new UIButton(4, titleHeight, w-10, 20) {
+ super(lx.ui, "TEMPO", x, y, w, h);
+ tempoButton = new UIButton(4, UIWindow.TITLE_LABEL_HEIGHT, w-10, 20) {
protected void onToggle(boolean active) {
if (active) {
lx.tempo.tap();
}
}.setMomentary(true);
tempoButton.addToContainer(this);
+ new UITempoBlipper(8, UIWindow.TITLE_LABEL_HEIGHT + 5, 12, 12).addToContainer(this);
}
- public void draw() {
- tempoButton.setLabel("" + ((int)(lx.tempo.bpm() * 10)) / 10.);
- super.draw();
-
- // Overlay tempo thing with openGL, redraw faster than button UI
- fill(color(0, 0, 24 - 8*lx.tempo.rampf()));
- noStroke();
- rect(x + 8, y + titleHeight + 5, 12, 12);
+ class UITempoBlipper extends UIObject {
+ UITempoBlipper(float x, float y, float w, float h) {
+ super(x, y, w, h);
+ }
+
+ void onDraw(UI ui, PGraphics pg) {
+ tempoButton.setLabel("" + ((int)(lx.tempo.bpm() * 10)) / 10.);
+
+ // Overlay tempo thing with openGL, redraw faster than button UI
+ pg.fill(color(0, 0, 24 - 8*lx.tempo.rampf()));
+ pg.noStroke();
+ pg.rect(0, 0, width, height);
+
+ redraw();
+ }
}
+
}
class UIMapping extends UIWindow {
private final UIIntegerBox stripBox;
UIMapping(MappingTool tool, float x, float y, float w, float h) {
- super("MAPPING", x, y, w, h);
+ super(lx.ui, "MAPPING", x, y, w, h);
mappingTool = tool;
- int yp = titleHeight;
+ int yp = UIWindow.TITLE_LABEL_HEIGHT;
new UIToggleSet(4, yp, w-10, 20) {
protected void onToggle(String value) {
if (value == MAP_MODE_ALL) mappingTool.mappingMode = mappingTool.MAPPING_MODE_ALL;
yp += 10;
- new UIScrollList(1, yp, w-2, 60).setItems(Arrays.asList(new ScrollItem[] {
+ new UIScrollList(1, yp, w-2, 60).setItems(Arrays.asList(new UIScrollList.Item[] {
new ColorScrollItem(ColorScrollItem.COLOR_RED),
new ColorScrollItem(ColorScrollItem.COLOR_GREEN),
new ColorScrollItem(ColorScrollItem.COLOR_BLUE),
stripBox.setValue(value);
}
- class ColorScrollItem extends AbstractScrollItem {
+ class ColorScrollItem extends UIScrollList.AbstractItem {
public static final int COLOR_RED = 1;
public static final int COLOR_GREEN = 2;
private String line2 = "";
UIDebugText(float x, float y, float w, float h) {
- super(x, y, w, h);
+ super(lx.ui, x, y, w, h);
}
public UIDebugText setText(String line1) {
return this;
}
- protected void onDraw(PGraphics pg) {
- super.onDraw(pg);
+ protected void onDraw(UI ui, PGraphics pg) {
+ super.onDraw(ui, pg);
if (line1.length() + line2.length() > 0) {
pg.noStroke();
pg.fill(#444444);
- pg.rect(0, 0, w, h);
- pg.textFont(defaultItemFont);
+ pg.rect(0, 0, width, height);
+ pg.textFont(ui.getItemFont());
pg.textSize(10);
pg.textAlign(LEFT, TOP);
pg.fill(#cccccc);
final BasicParameter speed;
UISpeed(float x, float y, float w, float h) {
- super("SPEED", x, y, w, h);
+ super(lx.ui, "SPEED", x, y, w, h);
speed = new BasicParameter("SPEED", 0.5);
- new UIParameterSlider(4, titleHeight, w-10, 20)
- .setParameter(speed.addListener(new LXParameterListener() {
+ speed.addListener(new LXParameterListener() {
public void onParameterChanged(LXParameter parameter) {
lx.setSpeed(parameter.getValuef() * 2);
}
- })).addToContainer(this);
+ });
+ new UISlider(4, UIWindow.TITLE_LABEL_HEIGHT, w-10, 20).setParameter(speed).addToContainer(this);
}
}
private final UIButton logMode;
UIMidi(final MidiEngine midiEngine, float x, float y, float w, float h) {
- super("MIDI", x, y, w, h);
+ super(lx.ui, "MIDI", x, y, w, h);
// Processing compiler doesn't seem to get that list of class objects also conform to interface
- List<ScrollItem> scrollItems = new ArrayList<ScrollItem>();
+ List<UIScrollList.Item> scrollItems = new ArrayList<UIScrollList.Item>();
for (SCMidiInput mc : midiEngine.getControllers()) {
scrollItems.add(mc);
}
final UIScrollList scrollList;
- (scrollList = new UIScrollList(1, titleHeight, w-2, 100)).setItems(scrollItems).addToContainer(this);
+ (scrollList = new UIScrollList(1, UIWindow.TITLE_LABEL_HEIGHT, w-2, 100)).setItems(scrollItems).addToContainer(this);
(deckMode = new UIToggleSet(4, 130, 90, 20) {
protected void onToggle(String value) {
midiEngine.setFocusedDeck(value == "A" ? 0 : 1);